I can't understand why sometimes typescript isn't able to infer the generic type of a const.
This is an example:
type OneTwoThree = 1 | 2 | 3;
type MyType<num extends OneTwoThree> = {
n: num;
}
const first: MyType = { // <-- Generic type 'MyType' requires 1 type argument(s).(2314)
n: 2,
};
const second: MyType<3> = {
n: 3,
};
Why typescript can't infer that first
is of type MyType<2>
?
I've also tried declaring MyType
in this way:
type MyType<num extends OneTwoThree = OneTwoThree> = {
n: num;
}
But in this way first become of type const first: MyType<OneTwoThree>
...
This is the playground link: https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gdhAKgdwPaIBYCcLQLxQCMUAPlAEylQDMA3AFD2iRQCyIi4EAPHAK4BbKBAAewCHAAmAZ1gIU6bLigF4SNJhwQAfCqgBvelGNQ4ALlOCGAX0YBjVHGnAoAMwCWWZxfacWBQxNTC3IAGnprBnoHJxdpCBjJHw4ubmpdAKMTcxpwyKA
Any suggestions?
CodePudding user response:
This is a missing feature of TypeScript; the compiler does not infer type arguments in generic types, like the N
in your MyType<N>
. As you note, using a generic parameter default does not do this; when you use defaults, you get the default and not type inference. Only when you call generic functions will the compiler try to infer type arguments.
There is a fairly longstanding open request at microsoft/TypeScript#32794 to support type argument inference in generic types, but for now it's not part of the language. Note that because generic parameter defaults exist, you wouldn't be able to get this like const first: MyType = ...
. You might be able to write something like const first: MyType<infer> = ...
, and possibly rewrite the default so that it was like type MyType<N extends OneTwoThree = infer> = ...
, but this is just speculation, since it's not a feature yet.
Until and unless such a feature is provided, the usual workaround is to write a generic helper identity function. The function doesn't do anything other than return its input, but when you call a generic function you get the inference you're looking for. So instead of const first: MyType = {...}
, you'd write const first = asMytype({...})
:
const asMyType = <N extends OneTwoThree>(m: MyType<N>) => m;
const first = asMyType({
n: 2,
});
// const first: MyType<2>
CodePudding user response:
You can do this with a function :
function foo<T extends OneTwoThree>(param: T): MyType<T> {
return { n: param }
}
const bar = foo(3); // MyType<number>
const baz = foo(3 as const); // MyType<3>