The different subtype error is usually caused by two mistakes:
- Providing a default value which might not be specific enough.
function broke<T extends string>(x: T = "error"): T { return x }
// ^^^^^^^^^^^^^^
// Type 'string' is not assignable to type 'T'.
// 'string' is assignable to the constraint of type 'T', but
// 'T' could be instantiated with a different subtype of
// constraint 'string'.
This isn't valid because someone can call your function as broke<"x">()
, in which case the returned value must be "x"
but the default can't determine this.
- Providing an implementation which allows the generic type to be limited too early.
interface Identity {
identity<T>(x: T): T
}
class Child<T> implements Identity {
identity(x: T): T { return x }
// ^^^^^^^^
// Property 'identity' in type 'Child<T>' is not assignable
// to the same property in base type 'Identity'.
// ...
// Type 'T' is not assignable to type 'T'. Two different
// types with this name exist, but they are unrelated.
// 'T' could be instantiated with an arbitrary type
// which could be unrelated to 'T'.
}
This isn't valid, because even if I have a Child, I should be able to call identity with some other type.
const child = new Child<string>()
child.identity(11) // ok for Identity, but not for Child
Some rules of thumb to keep in mind:
- Ensure the function works for all possible values of
T
. IfT
extendsstring
, consider the user supplyingT = string
,T = "x"
, andT = never
. - Review if the function must be generic.
Sometimes this error might theoretically be a problem, but you can guarantee that in reality, it will never be an issue. If so, you can use overloads to remove the subtype checking.
function broke<T extends string>(x?: T): T
function broke(x: string = "error"): string { // unsafe, no error
return x;
}