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
typeNominalString=string&{__nomimalStringBrand: any}// Brand for nominal typingconstnominal='nominal'asNominalString;letnominalRecord: Record<NominalString,string>={};nominalRecord={normal: 'normal'// Should be an error};nominalRecord={[nominal]: 'nominal'};nominalRecord['normal'];// Should be an error (even without noImplicitAny)nominalRecord[nominal];// Should work (even with noImplicitAny)
Expected behavior:
Lines commented as should be an error, should be. Lines commented as should work, should not be an error.
Actual behavior:
Compile time errors do not match with expectations.
The mapped type Record<K, T> creates a type with keys in K with type T. if K is a set of literal types then each entry translates to a property name. there is a special case for the string type to add a n index signature on the resulting type to T.
The behavior in the sample above is not correct, since the compiler does not detect that it is the string type because of the intersection, and then does nothing. So it should behave like Record<string, string> i.e. { [x:stirng]: T }.
The current behavior or Record takes the known literal types in K, which in this case is the empty set and creates a type for you, and the resulting type is {}. That is why nominalRecord = { normal: 'normal' } works, cause { normal: 'normal' } is assignable to {}; as a matter of fact, any thing is assignable to {}, so you could also have written nominalRecord = 1 or nominalRecord = "my other string"; and you would not have gotten errors. see more about why this happens in the FAQs.
I do not think this is what you were looking for anyways, you were looking for restricting the mapping on an object to a subtype of string (tagged string), that is not a literal type. this has been discussed before in #1778
and #2491 (disregard the enum part of the request).
We have discussed allowing indexing with some subtype of number or string; but we could not come up with a scheme that make sense; given that:
all types in TS are open, i.e. you can add more properties, e.g.:
vary={a: 1,b:2};varx: {a: number}=y;// OK, since y has an "a" that is a number
and 2. that all JS objects are maps by definition and you can always index into an object using any good old string.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
I'm facing a similar issue with TS 2.6.2 or 2.8.0-dev:
typeUserId=string&{__isUserId: true}typeUser={id: UserId,name: string}typeUsers=Record<UserId,User>varid=0functionnextId(){return''+id++asUserId}constusers={}asUsersconstjoe={id: nextId(),name: 'Joe'}// Error: Users have no index signatureusers[joe.id]=joe
I think this is quite a common use case nowadays as using strings everywhere lacks semantic/meaning/type safety.
TypeScript Version: 2.3
Code
Note: Be sure to enable
noImplicitAny
Typescript Playground
Expected behavior:
Lines commented as should be an error, should be. Lines commented as should work, should not be an error.
Actual behavior:
Compile time errors do not match with expectations.
Related Issues: #202
The text was updated successfully, but these errors were encountered: