Reputation: 177
export interface Foo<T extends string | object | null> {
bar: T;
}
type Callback<T = any> = (res: T) => void
function hydra<T extends string | object | null>(callback: Callback<Foo<T>>) {
callback({
bar: 'Hello World'
});
}
I doesn't work. It throws this error:
Type '"Hello World"' is not assignable to type 'T'.
'"Hello World"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | object | null'.
How can I solve this issue?
Upvotes: 0
Views: 87
Reputation: 71911
You can make a subtype which contains your constraints, and have that declared as the value of bar
. This way you don't have the constraint issues where T
could be something else than the provided string:
export type FooType = string | object | null;
export interface Foo {
bar: FooType;
}
type Callback<T = any> = (res: T) => void
function returnResponse(callback: Callback<Foo>) {
callback({
bar: 'INVAILD_REQUEST'
});
}
This will make your lose your generic type hinting though. So another solution would be to use as T
.
export interface Foo<T extends string | object | null> {
bar: T;
}
type Callback<T = any> = (res: T) => void
function hydra<T extends string | object | null>(callback: Callback<Foo<T>>) {
callback({
bar: 'Hello World' as T
});
}
The reason you have to do that is because if a consumer of the hydra function passes in a callback with a different generic type, your type string
does not suffice anymore. Like the error message says:
'"Hello World"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | object | null'.
If you have an error like this, you always need to do a as T
to make the compiler happy
Upvotes: 0
Reputation: 1051
The main problem here is, although the literal Hello World
is assignable to a string
, the fact of having a generic argument (as a constraint) relies on having possibly a subtype of string
which Hello World
would not satisfy.
Suppose this scenario
type StringSubtype = 'Not Hello World';
hydra<StringSubtype>(res => {
// ...
});
The generic argument of the function hydra
is now a subset of string
. And, indeed, it satisfies the extends string | object | null
constraint. All valid.
But, res.bar
is of type 'Not Hello World'
which means not other string value can match the type.
hydra<StringSubtype>(res => {
// ...
// Type '"Hello World"' is not assignable to type '"Not Hello World"'
res = {
bar: `Hello World`
};
});
This is what your compilation error says
Type '"Hello World"' is not assignable to type 'T'. '"Hello World"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | object | null'.
Even if Hello World
is assignable to string
, the compiler does not guarantee that T
could be a subtype of string
.
Solution
Simply don't declare your hydra
function as generic. Use the more flexible union type as a type argument of Foo<>
directly.
function hydra(callback: Callback<Foo<string | object | null>>) {
callback({
bar: 'Hello World'
});
}
EDIT: Alternative solutions
Hello World
as T
function hydra<T extends string | object | null>(callback: Callback<Foo<T>>) {
callback({
bar: 'Hello World' as unknown as T
});
}
T
with the forced valuefunction hydra<T extends string | object | null>(callback: Callback<Foo<T | 'Hello World'>>) {
callback({
bar: 'Hello World'
});
}
But, deeply enough, if you are forcing a value to res.bar
, there's no reason to have any generic argument.
However, your example is a sample and I guess there should be a reason for this... I hope any of these three workarounds matches with your intention.
I explained what the error was, at least. So you can achieve what you need with this knowledge, I guess.
Hope it helps.
Upvotes: 1