Reputation: 14318
I have a function that, simplified, takes in a string literal and a callback that returns { type: [mystringliteral] }
.
function myFunc<T>(type: T, callback: () => { type: T }): ReturnType<typeof callback> {
return { type };
}
Unless I specifically set T
when making the call, T
is inferred to be string
rather than the literal, and there's no type enforcement on the type
property.
// successfully raises compilation error
const x = myFunc<'abc'>('abc', () => ({ type: 'def' }));
// does not raise an error
const y = myFunc('abc', () => ({ type: 'def' }));
I'm writing a declaration file for a third party npm package, so I can't call any clever functions to hint to the compiler what's going on.
I'm guessing what I'm asking is flat out impossible with the current TypeScript spec (it would be nice if we could do something like <T extends string literal>
), but I figured I'd ask.
Upvotes: 1
Views: 70
Reputation: 249726
There are two issues here. The first one is to get the compiler to infer string literal types. For this, we need to add a constraint of string
to T
. This will hint to the compiler that we want literal types.
This will not be sufficient however. Just adding T extends string
to the signature will make the compiler infer a union of 'abc' | 'def'
and will still not produce an error. To get around this we must let the compiler know that the second appearance of T
should not be used for inference. While there is a proposal here to add syntax for this, it is not implemented yet. We can however use the workaround jcalz suggests to get the desired effect:
function myFunc<T extends string>(type: T, callback: () => { type: NoInfer<T> }): ReturnType<typeof callback> {
return { type };
}
type NoInfer<T> = [T][T extends any ? 0 : never];
// raises an error
const y = myFunc('abc', () => ({ type: 'def' }));
Upvotes: 2