-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Literal types don't work with generics as expected #12267
Comments
|
@rozzzly const x1 = 1; // x1: number
const x2: 1 = 1; // x2: 1 But in TypeScript 2.1 literal types are much cleaner and powerful: const x = 1; // x: 1 I expect similar behaviour for generics (like in issue description). |
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: const X: 'x' = 'x';
const ONE: 1 = 1;
const obj1 = create<'x', 1>('x', 1); // { type: 'x', data: 1 }
const obj2 = create('x' as 'x', 1 as 1); // { type: 'x', data: 1 }
const obj3 = create(X, ONE); // { type: 'x', data: 1 } For more discussion see #11126. |
I'm slightly confused. In the following const X: 'x' = 'x'; // "x"
const o = create(X, 1); // { type: "x", data: number }; the type of In the following, however, const X = 'x'; // still "x"
const o = create(X, 1); // { type: string, data: number }; the type of |
@aluanhaddad The difference between a |
@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. |
@ahejlsberg excellent. Thank you. |
@ahejlsberg I appreciate the clarification. It definitely makes sense to me now but on the whole this is an interesting and rather subtle behavior. |
@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: function create<T extends string, D extends number>(type: T, data: D) {
return { type, data };
}
const x = create('x', 2); // { type: "x", data: 2 }; It's only unconstrained generics which have the widening ambiguity issue. |
@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. |
@weswigham @ahejlsberg
In this example |
@weswigham Wonderful! I found your example can be extended to take arbitary literal types 👍 type TypesCanBeLiteral = number | string | boolean;
function create<T extends TypesCanBeLiteral, D extends TypesCanBeLiteral>(type: T, data: D) {
return { type, data };
}
const x = create('x', 2); //: { type: "x", data: 2 };
const y = create(1, true); //: { type: 1, data: true } |
TypeScript Version: master (cef9d85)
Code
Expected behavior:
obj
has type{ type: 'x', data: 1 }
Actual behavior:
obj
has type{ type: string, data: number }
The text was updated successfully, but these errors were encountered: