You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Literal types don't work with generics as expected #12267
const obj = create<'x', number>('x', 1) is what you're looking for. Typescript assumes you just intended for 'x' to be a string, not a literal. So if you want it to be typed with a literal, you have to explicitly mark that during your function call.
Unless there is some indication that you want the literal type preserved, TypeScript will widen the type when it is inferred for a mutable location (such as an object literal property). There are many ways you can indicate you want to preserve the literal type, including:
constX: 'x'='x';// "x"consto=create(X,1);// { type: "x", data: number };
the type of o.type is inferred as "x" via the type of X.
In the following, however,
constX='x';// still "x"consto=create(X,1);// { type: string, data: number };
the type of o.type is inferred as string seemingly because X is not annotated. This puzzles me because the type of X is exactly the same in both cases.
Aluan Haddad (@aluanhaddad) I should add that the intuitive way to think of this is that we will never widen a literal type that resulted from an explicit type annotation. We only widen implicit literal types.
Anders Hejlsberg (@ahejlsberg) I appreciate the clarification. It definitely makes sense to me now but on the whole this is an interesting and rather subtle behavior.
In the past (prior to this discussion) I would have flagged the explicit type annotation, say during a code review, as unnecessary. Obviously this is a matter of style and opinion but it is interesting that the annotation has this kind of second-order effect.
Aluan Haddad (@aluanhaddad) If you know what the domain of each type argument is supposed to be, specifying it like so will also cause literal types to be correctly inferred from literals:
Wesley Wigham (@weswigham) thank you for that. I like how the use of constraints makes the code more clear and at the same time appropriately places the authority of determining the literalness of the return type with the callee.
Wesley Wigham (@weswigham)
Thank you for this generics example. Is this code just a hack or a bug or a normal code/behaviour, that I can use for now and future?
Anders Hejlsberg (@ahejlsberg)
Thank you and all team for literal types. But after widening/non-widening (#11126) I need to write more code with more types in common cases. Example:
const x: 1 = 1; // because I want use x as 1, for example, in object literals { prop: x }
const y = x; // y: 1
let z = x; // z: 1
In this example const y and let z have absolutely the same behaviour. It's very strange for me. I need to write let smth: number everywhere. Because if I don't want mutable behaviour I will simply write const smth.
Activity
rozzzly commentedon Nov 15, 2016
const obj = create<'x', number>('x', 1)is what you're looking for. Typescript assumes you just intended for 'x' to be a string, not a literal. So if you want it to be typed with a literal, you have to explicitly mark that during your function call.arusakov commentedon Nov 15, 2016
Patrick Lienau (@rozzzly)
It likes old (TypeScript 2.0) literal types behaviour:
But in TypeScript 2.1 literal types are much cleaner and powerful:
I expect similar behaviour for generics (like in issue description).
ahejlsberg commentedon Nov 15, 2016
Unless there is some indication that you want the literal type preserved, TypeScript will widen the type when it is inferred for a mutable location (such as an object literal property). There are many ways you can indicate you want to preserve the literal type, including:
For more discussion see #11126.
aluanhaddad commentedon Nov 16, 2016
I'm slightly confused.
In the following
the type of
o.typeis inferred as"x"via the type ofX.In the following, however,
the type of
o.typeis inferred asstringseemingly becauseXis not annotated. This puzzles me because the type ofXis exactly the same in both cases.ahejlsberg commentedon Nov 16, 2016
Aluan Haddad (@aluanhaddad) The difference between a
constwith and without a type annotation is explained in detail in #11126.ahejlsberg commentedon Nov 16, 2016
Aluan Haddad (@aluanhaddad) I should add that the intuitive way to think of this is that we will never widen a literal type that resulted from an explicit type annotation. We only widen implicit literal types.
aluanhaddad commentedon Nov 16, 2016
Anders Hejlsberg (@ahejlsberg) excellent. Thank you.
aluanhaddad commentedon Nov 16, 2016
Anders Hejlsberg (@ahejlsberg) I appreciate the clarification. It definitely makes sense to me now but on the whole this is an interesting and rather subtle behavior.
In the past (prior to this discussion) I would have flagged the explicit type annotation, say during a code review, as unnecessary. Obviously this is a matter of style and opinion but it is interesting that the annotation has this kind of second-order effect.
weswigham commentedon Nov 16, 2016
Aluan Haddad (@aluanhaddad) If you know what the domain of each type argument is supposed to be, specifying it like so will also cause literal types to be correctly inferred from literals:
It's only unconstrained generics which have the widening ambiguity issue.
aluanhaddad commentedon Nov 16, 2016
Wesley Wigham (@weswigham) thank you for that. I like how the use of constraints makes the code more clear and at the same time appropriately places the authority of determining the literalness of the return type with the callee.
arusakov commentedon Nov 16, 2016
Wesley Wigham (@weswigham)
Thank you for this generics example. Is this code just a hack or a bug or a normal code/behaviour, that I can use for now and future?
Anders Hejlsberg (@ahejlsberg)
Thank you and all team for literal types. But after widening/non-widening (#11126) I need to write more code with more types in common cases. Example:
In this example
const yandlet zhave absolutely the same behaviour. It's very strange for me. I need to writelet smth: numbereverywhere. Because if I don't want mutable behaviour I will simply writeconst smth.lefb766 commentedon Mar 4, 2017
Wesley Wigham (@weswigham) Wonderful! I found your example can be extended to take arbitary literal types 👍