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
Bug: (Any) string is assignable to numeric templated literal type #40538
changed the title [-]Bug: (Any string) is assignable to templated literal type[/-][+]Bug: (Any string) is assignable to numeric templated literal type[/+]on Sep 14, 2020
changed the title [-]Bug: (Any string) is assignable to numeric templated literal type[/-][+]Bug: (Any) string is assignable to numeric templated literal type[/+]on Sep 14, 2020
This is working as intended, but could be a suggestion. The instantiation Foo<number> resolves to `${number}` which then resolves to string (we do the same when a placeholder is instantiated to string or bigint).
We could consider keeping templates such as `${number}` or `start-${string}-end` around and introduce assignability rules similar to what we do in type inference. I had some of that in place at one time, but was concerned it leads us down the slippery slope towards full on regular expression types. But we're sort of there already because of type inference, so maybe.
typeMatchDigit<Dextendsstring>=Dextends'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' ? D : never;typeMatchInteger<Sextendsstring,T=S>=Sextends `${MatchDigit<infer _D>}${infer R}` ? Rextends'' ? T : MatchInteger<R,T> : never;typeMatchDecimal<Sextendsstring>=Sextends `${''|'+'|'-'}${MatchInteger<infer _I>}` ? S :
Sextends `${''|'+'|'-'}${MatchInteger<infer _I>}.${MatchInteger<infer _F>}` ? S :
never;typeMatchExtent<Sextendsstring>=Sextends `${MatchDecimal<infer _>}${'px'|'pt'}` ? S : never;declarefunctiontakeExtent<Sextendsstring>(ex: MatchExtent<S>): void;takeExtent('100px');takeExtent('-1px');takeExtent('9.5pt');
Note the trick of applying the matching productions to the infer X placeholders. This works as long as each production resolves to its own type argument in one of its branches (because when inferring to a conditional type we infer to each of the branches). Also note that the validation happens during type inference, not during relationship checking. That means it isn't possible to declare a type Extent and have validation occur in assignments.
That Foo just resolves to a string comes as a surprise.
Your construct above still looks to convoluted for such a common use case (“5px”, “2.5em”) and having a short straightforward way to declare such a type would be great.
EDIT: and for sure it should also work at assignment stage, so that we can define interfaces that have fields with templates types.
Activity
[-]Bug: (Any string) is assignable to templated literal type[/-][+]Bug: (Any string) is assignable to numeric templated literal type[/+][-]Bug: (Any string) is assignable to numeric templated literal type[/-][+]Bug: (Any) string is assignable to numeric templated literal type[/+]RyanCavanaugh commentedon Sep 14, 2020
Anders Hejlsberg (@ahejlsberg) how is this supposed to work?
tpict commentedon Sep 14, 2020
Isn’t this intended behaviour?
derolf commentedon Sep 14, 2020
If it works as intended, how can I define a string-type that contains only numbers and has a certain suffix?
Example: ‘42px’
See: https://stackoverflow.com/q/63879463/3612643
ahejlsberg commentedon Sep 14, 2020
This is working as intended, but could be a suggestion. The instantiation
Foo<number>resolves to`${number}`which then resolves tostring(we do the same when a placeholder is instantiated tostringorbigint).We could consider keeping templates such as
`${number}`or`start-${string}-end`around and introduce assignability rules similar to what we do in type inference. I had some of that in place at one time, but was concerned it leads us down the slippery slope towards full on regular expression types. But we're sort of there already because of type inference, so maybe.tpict commentedon Sep 14, 2020
I wrote this for my own project, there’s certainly room for improvement though.
This is bound to be such a common application of template literals that I do think it warrants something more ergonomic and performant.
ahejlsberg commentedon Sep 14, 2020
Here's how I'd write it:
Note the trick of applying the matching productions to the
infer Xplaceholders. This works as long as each production resolves to its own type argument in one of its branches (because when inferring to a conditional type we infer to each of the branches). Also note that the validation happens during type inference, not during relationship checking. That means it isn't possible to declare a typeExtentand have validation occur in assignments.tpict commentedon Sep 14, 2020
That’s a neat trick, thanks!
derolf commentedon Sep 15, 2020
Anders Hejlsberg (@ahejlsberg) thanks for the proposal, but two remarks:
That Foo just resolves to a string comes as a surprise.
Your construct above still looks to convoluted for such a common use case (“5px”, “2.5em”) and having a short straightforward way to declare such a type would be great.
EDIT: and for sure it should also work at assignment stage, so that we can define interfaces that have fields with templates types.
derolf commentedon Sep 24, 2020
Fixed by #40598