Skip to content
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

More specific inference for constrained 'infer' types in template literal types #48094

Merged
merged 10 commits into from
May 27, 2022

Conversation

rbuckton
Copy link
Member

@rbuckton rbuckton commented Mar 2, 2022

This modifies inference in template literal types when inferring to a constrained type variable by leveraging the constraint of the type variable to infer a more specific type:

// helper that enforces a constraint on an `infer T` type
type Is<T extends U, U> = T;

// today
type T0 = "100" extends `${Is<infer T, number>}` ? T : never; // number
type T1 = "100" extends `${Is<infer T, bigint>}` ? T : never; // bigint
type T2 = "true" extends `${Is<infer T, boolean>}` ? T : never; // boolean

// after this change
type T0 = "100" extends `${Is<infer T, number>}` ? T : never; // 100
type T1 = "100" extends `${Is<infer T, bigint>}` ? T : never; // 100n
type T2 = "true" extends `${Is<infer T, boolean>}` ? T : never; // true

The primary motivation for this feature is converting numeric string index keys from a tuple type into their numeric literal equivalents. While you can use something like a mapping table for a subset of numbers (i.e., as many as you are willing to manually define), that can run into issues when that might then generate a too-large union. Rather than introducing a feature specific to numeric string index keys, this instead focuses on a more general purpose solution that can apply to any valid template literal type placeholder (i.e., string, number, bigint, boolean, null, and undefined).

Please note that this approach cannot be used to convert an arbitrary numeric string into a number or bigint literal type, as the inferred type must still satisfy the extends condition in the conditional type. This means that a string such as "0x10" cannot be converted to a number, since that value would not round-trip: If you attempt to infer a number literal type from "0x10", the result would be 16, however when 16 is substituted back into the string literal type it becomes "16", which is not a supertype of "0x10".

Also note that this PR does not introduce any new mechanism for establishing a constraint for an infer T type. Instead, a helper type like Is (above) might be necessary to enforce the constraint. A syntactic mechanism to constrain infer T would be valuable, but is out of scope for this PR.

Addendum: If we decide to take #48112, the above example could be rewritten using infer T extends ... instead of the Is type alias:

type T0 = "100" extends `${infer T extends number}` ? T : never; // 100
type T1 = "100" extends `${infer T extends bigint}` ? T : never; // 100n
type T2 = "true" extends `${infer T extends boolean}` ? T : never; // true

Fixes #47141

@treybrisbane
Copy link

The primary motivation for this feature is converting numeric string index keys from a tuple type into their numeric literal equivalents.

Could a new intrinsic like

type ParseInt<AString extends string, Radix extends number = 10> = intrinsic;

solve that problem?

The reason I ask is that a ParseInt intrinsic is something I've been considering proposing, but I haven't yet gotten around to doing a proper write-up for it. 😅

@rbuckton
Copy link
Member Author

Could a new intrinsic like [...] solve that problem?

I'd considered that approach, however the intrinsic type functionality is very strongly tied to string literal types currently and would require quite a bit of refactoring to change. Its something we are likely to do in the future, but I felt the general purpose approach above to be both adequate for numeric string index keys (which are values that are round-trippable), as well as more flexible for working with other placeholder types like bigint/boolean.

@sno2
Copy link
Contributor

sno2 commented Mar 12, 2022

Hello, thank you very much for your work. Would the following behavior be correct with this PR?

type NumToBigint<T extends number> = `${T}` extends `${Is<infer $BN, bigint>}` ? $BN : never;

type asdf = NumToBigint<23>; // 23n
type asdf2 = NumToBigint<23.5>; // never
type adsf5 = NumToBigint<-2>; // -2n

@rbuckton
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 14, 2022

Heya @rbuckton, I've started to run the tarball bundle task on this PR at ed2be49. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

Hey @rbuckton, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/121826/artifacts?artifactName=tgz&fileId=50F732BE8D5A25274F80CDE406275C2EBB6AA3559A8E66687CB9BBAFF38E6CF602&fileName=/typescript-4.7.0-insiders.20220314.tgz"
    }
}

and then running npm install.

@rbuckton
Copy link
Member Author

Hello, thank you very much for your work. Would the following behavior be correct with this PR?

type NumToBigint<T extends number> = `${T}` extends `${Is<infer $BN, bigint>}` ? $BN : never;

type asdf = NumToBigint<23>; // 23n
type asdf2 = NumToBigint<23.5>; // never
type adsf5 = NumToBigint<-2>; // -2n

Yes, that is correct.

@rbuckton rbuckton requested a review from sandersn March 18, 2022 23:41
@ahejlsberg
Copy link
Member

Additionally, might be good to have some tests that check inference to constrained type parameters of functions. For example:

declare function test<T extends string | number>(s: `**${T}**`): T;
const x = test('**123**');  // Should produce "123" | 123

@rbuckton
Copy link
Member Author

Additionally, might be good to have some tests that check inference to constrained type parameters of functions. For example:

declare function test<T extends string | number>(s: `**${T}**`): T;
const x = test('**123**');  // Should produce "123" | 123

This doesn't seem to work. We correctly produce inference candidates of "123" and 123, but getCovariantInference returns only "123", which seems to be due to getCommonSupertype/getSupertypeOrUnion picking only the left-most literal type since the candidates don't share a common supertype.

@rbuckton
Copy link
Member Author

Ah, I may just need to add a new inference priority to indicate an inference to a template type placeholder.

@rbuckton
Copy link
Member Author

I've added a new InferencePriority for template type placeholders and included it in PriorityImpliesCombination so that we produce the union in getCovariantInference rather than just the first literal type in the union.

@rbuckton
Copy link
Member Author

@typescript-bot perf test
@typescript-bot run dt
@typescript-bot test this
@typescript-bot user test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 22, 2022

Heya @rbuckton, I've started to run the perf test suite on this PR at df23e95. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 22, 2022

Heya @rbuckton, I've started to run the parallelized Definitely Typed test suite on this PR at df23e95. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 22, 2022

Heya @rbuckton, I've started to run the extended test suite on this PR at df23e95. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 22, 2022

Heya @rbuckton, I've started to run the parallelized community code test suite on this PR at df23e95. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

@rbuckton
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..48094

Metric main 48094 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 356,348k (± 0.02%) 356,435k (± 0.03%) +86k (+ 0.02%) 356,206k 356,575k
Parse Time 2.07s (± 0.72%) 2.05s (± 0.52%) -0.02s (- 1.01%) 2.03s 2.08s
Bind Time 0.86s (± 1.02%) 0.86s (± 0.55%) 0.00s ( 0.00%) 0.85s 0.87s
Check Time 5.76s (± 0.59%) 5.74s (± 0.41%) -0.02s (- 0.40%) 5.68s 5.79s
Emit Time 5.99s (± 0.61%) 5.98s (± 0.69%) -0.01s (- 0.17%) 5.91s 6.06s
Total Time 14.69s (± 0.52%) 14.63s (± 0.37%) -0.06s (- 0.38%) 14.52s 14.76s
Compiler-Unions - node (v10.16.3, x64)
Memory used 205,723k (± 0.03%) 205,713k (± 0.04%) -10k (- 0.00%) 205,526k 205,914k
Parse Time 0.84s (± 0.98%) 0.84s (± 0.90%) +0.00s (+ 0.12%) 0.82s 0.86s
Bind Time 0.52s (± 1.67%) 0.52s (± 1.63%) +0.00s (+ 0.19%) 0.51s 0.55s
Check Time 7.87s (± 0.34%) 7.95s (± 0.79%) +0.08s (+ 0.97%) 7.88s 8.19s
Emit Time 2.51s (± 0.74%) 2.52s (± 0.87%) +0.01s (+ 0.32%) 2.46s 2.57s
Total Time 11.75s (± 0.31%) 11.83s (± 0.60%) +0.09s (+ 0.73%) 11.73s 12.09s
Monaco - node (v10.16.3, x64)
Memory used 343,674k (± 0.01%) 343,726k (± 0.02%) +52k (+ 0.02%) 343,556k 343,835k
Parse Time 1.57s (± 0.69%) 1.57s (± 0.58%) -0.00s (- 0.06%) 1.55s 1.59s
Bind Time 0.76s (± 0.87%) 0.75s (± 0.48%) -0.01s (- 0.79%) 0.75s 0.76s
Check Time 5.72s (± 0.53%) 5.67s (± 0.53%) -0.05s (- 0.87%) 5.61s 5.76s
Emit Time 3.23s (± 0.50%) 3.23s (± 0.61%) +0.00s (+ 0.03%) 3.20s 3.29s
Total Time 11.28s (± 0.40%) 11.23s (± 0.37%) -0.05s (- 0.48%) 11.15s 11.32s
TFS - node (v10.16.3, x64)
Memory used 305,351k (± 0.03%) 305,341k (± 0.03%) -10k (- 0.00%) 305,110k 305,564k
Parse Time 1.28s (± 0.52%) 1.28s (± 0.79%) +0.00s (+ 0.39%) 1.27s 1.32s
Bind Time 0.71s (± 0.70%) 0.72s (± 0.66%) +0.00s (+ 0.28%) 0.71s 0.73s
Check Time 5.18s (± 0.33%) 5.18s (± 0.41%) +0.01s (+ 0.17%) 5.12s 5.22s
Emit Time 3.45s (± 1.29%) 3.45s (± 1.25%) +0.00s (+ 0.14%) 3.36s 3.57s
Total Time 10.62s (± 0.49%) 10.64s (± 0.41%) +0.02s (+ 0.18%) 10.55s 10.72s
material-ui - node (v10.16.3, x64)
Memory used 476,212k (± 0.02%) 476,195k (± 0.01%) -17k (- 0.00%) 476,099k 476,299k
Parse Time 1.81s (± 0.40%) 1.81s (± 0.51%) -0.00s (- 0.22%) 1.79s 1.83s
Bind Time 0.68s (± 0.88%) 0.67s (± 1.12%) -0.00s (- 0.30%) 0.65s 0.69s
Check Time 22.90s (± 1.07%) 22.75s (± 0.44%) -0.15s (- 0.68%) 22.56s 23.01s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 25.39s (± 0.95%) 25.23s (± 0.40%) -0.16s (- 0.63%) 25.05s 25.52s
xstate - node (v10.16.3, x64)
Memory used 571,839k (± 0.02%) 571,781k (± 0.01%) -59k (- 0.01%) 571,626k 571,956k
Parse Time 2.58s (± 0.46%) 2.58s (± 0.41%) -0.00s (- 0.16%) 2.55s 2.60s
Bind Time 1.00s (± 0.59%) 1.02s (± 0.78%) +0.02s (+ 1.79%) 1.00s 1.04s
Check Time 1.51s (± 0.33%) 1.52s (± 0.32%) +0.00s (+ 0.13%) 1.51s 1.53s
Emit Time 0.07s (± 3.14%) 0.07s (± 0.00%) -0.00s (- 1.41%) 0.07s 0.07s
Total Time 5.16s (± 0.33%) 5.18s (± 0.32%) +0.02s (+ 0.31%) 5.14s 5.22s
Angular - node (v12.1.0, x64)
Memory used 334,188k (± 0.03%) 334,147k (± 0.01%) -41k (- 0.01%) 334,044k 334,219k
Parse Time 2.05s (± 0.59%) 2.04s (± 0.52%) -0.00s (- 0.05%) 2.02s 2.07s
Bind Time 0.83s (± 0.89%) 0.83s (± 0.58%) -0.00s (- 0.36%) 0.82s 0.84s
Check Time 5.56s (± 0.30%) 5.59s (± 0.47%) +0.03s (+ 0.54%) 5.54s 5.66s
Emit Time 6.21s (± 0.86%) 6.23s (± 0.63%) +0.02s (+ 0.29%) 6.15s 6.34s
Total Time 14.65s (± 0.45%) 14.69s (± 0.40%) +0.04s (+ 0.27%) 14.55s 14.82s
Compiler-Unions - node (v12.1.0, x64)
Memory used 193,327k (± 0.08%) 193,082k (± 0.12%) -246k (- 0.13%) 192,623k 193,473k
Parse Time 0.84s (± 0.73%) 0.84s (± 0.57%) -0.00s (- 0.48%) 0.83s 0.85s
Bind Time 0.53s (± 0.84%) 0.53s (± 0.64%) +0.00s (+ 0.19%) 0.53s 0.54s
Check Time 7.38s (± 0.36%) 7.37s (± 0.42%) -0.01s (- 0.14%) 7.31s 7.45s
Emit Time 2.56s (± 0.93%) 2.53s (± 0.86%) -0.03s (- 1.02%) 2.49s 2.58s
Total Time 11.31s (± 0.39%) 11.28s (± 0.37%) -0.04s (- 0.31%) 11.17s 11.35s
Monaco - node (v12.1.0, x64)
Memory used 326,637k (± 0.02%) 326,500k (± 0.11%) -137k (- 0.04%) 325,488k 326,888k
Parse Time 1.56s (± 1.01%) 1.55s (± 0.92%) -0.01s (- 0.45%) 1.52s 1.58s
Bind Time 0.74s (± 0.83%) 0.74s (± 0.49%) +0.00s (+ 0.40%) 0.74s 0.75s
Check Time 5.55s (± 0.35%) 5.55s (± 0.50%) +0.00s (+ 0.04%) 5.51s 5.64s
Emit Time 3.24s (± 0.59%) 3.27s (± 1.08%) +0.02s (+ 0.74%) 3.20s 3.39s
Total Time 11.09s (± 0.32%) 11.11s (± 0.57%) +0.02s (+ 0.21%) 11.03s 11.33s
TFS - node (v12.1.0, x64)
Memory used 289,973k (± 0.02%) 290,015k (± 0.03%) +42k (+ 0.01%) 289,851k 290,157k
Parse Time 1.29s (± 0.90%) 1.29s (± 0.58%) +0.00s (+ 0.00%) 1.27s 1.30s
Bind Time 0.70s (± 0.74%) 0.71s (± 1.13%) +0.01s (+ 1.00%) 0.69s 0.72s
Check Time 5.14s (± 0.45%) 5.10s (± 0.46%) -0.04s (- 0.68%) 5.07s 5.16s
Emit Time 3.47s (± 1.06%) 3.47s (± 1.67%) -0.00s (- 0.12%) 3.38s 3.66s
Total Time 10.61s (± 0.45%) 10.57s (± 0.57%) -0.03s (- 0.31%) 10.45s 10.73s
material-ui - node (v12.1.0, x64)
Memory used 455,025k (± 0.09%) 455,140k (± 0.06%) +115k (+ 0.03%) 454,028k 455,369k
Parse Time 1.80s (± 0.45%) 1.81s (± 0.50%) +0.00s (+ 0.11%) 1.79s 1.82s
Bind Time 0.64s (± 0.56%) 0.64s (± 0.62%) -0.00s (- 0.47%) 0.63s 0.65s
Check Time 20.45s (± 1.29%) 20.18s (± 0.52%) -0.27s (- 1.31%) 19.99s 20.48s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 22.89s (± 1.17%) 22.63s (± 0.45%) -0.27s (- 1.17%) 22.44s 22.91s
xstate - node (v12.1.0, x64)
Memory used 537,642k (± 0.01%) 537,511k (± 0.02%) -131k (- 0.02%) 537,281k 537,724k
Parse Time 2.53s (± 0.51%) 2.53s (± 0.38%) -0.00s (- 0.04%) 2.50s 2.54s
Bind Time 1.03s (± 0.56%) 1.03s (± 0.64%) -0.00s (- 0.29%) 1.02s 1.05s
Check Time 1.46s (± 0.52%) 1.47s (± 0.52%) +0.00s (+ 0.14%) 1.45s 1.49s
Emit Time 0.07s (± 0.00%) 0.07s (± 0.00%) 0.00s ( 0.00%) 0.07s 0.07s
Total Time 5.09s (± 0.45%) 5.09s (± 0.38%) +0.00s (+ 0.06%) 5.04s 5.12s
Angular - node (v14.15.1, x64)
Memory used 332,461k (± 0.01%) 332,418k (± 0.01%) -44k (- 0.01%) 332,354k 332,482k
Parse Time 2.01s (± 0.60%) 2.01s (± 0.34%) +0.00s (+ 0.00%) 2.00s 2.03s
Bind Time 0.86s (± 0.42%) 0.87s (± 0.42%) +0.00s (+ 0.23%) 0.86s 0.87s
Check Time 5.59s (± 0.46%) 5.59s (± 0.40%) -0.00s (- 0.04%) 5.53s 5.63s
Emit Time 6.28s (± 0.57%) 6.25s (± 0.39%) -0.03s (- 0.48%) 6.18s 6.31s
Total Time 14.75s (± 0.38%) 14.72s (± 0.23%) -0.03s (- 0.20%) 14.65s 14.79s
Compiler-Unions - node (v14.15.1, x64)
Memory used 194,901k (± 0.38%) 193,568k (± 0.62%) -1,332k (- 0.68%) 191,925k 195,282k
Parse Time 0.86s (± 0.87%) 0.86s (± 0.58%) -0.00s (- 0.12%) 0.85s 0.87s
Bind Time 0.56s (± 0.40%) 0.56s (± 0.59%) +0.00s (+ 0.18%) 0.55s 0.57s
Check Time 7.45s (± 0.75%) 7.42s (± 0.47%) -0.03s (- 0.35%) 7.35s 7.50s
Emit Time 2.49s (± 0.96%) 2.48s (± 1.41%) -0.01s (- 0.44%) 2.43s 2.61s
Total Time 11.36s (± 0.51%) 11.32s (± 0.55%) -0.04s (- 0.33%) 11.22s 11.51s
Monaco - node (v14.15.1, x64)
Memory used 325,411k (± 0.00%) 325,405k (± 0.00%) -6k (- 0.00%) 325,384k 325,435k
Parse Time 1.58s (± 0.70%) 1.57s (± 0.44%) -0.02s (- 1.20%) 1.55s 1.58s
Bind Time 0.78s (± 1.28%) 0.77s (± 0.47%) -0.01s (- 0.77%) 0.77s 0.78s
Check Time 5.49s (± 0.51%) 5.48s (± 0.59%) -0.01s (- 0.13%) 5.43s 5.56s
Emit Time 3.31s (± 0.55%) 3.33s (± 0.77%) +0.01s (+ 0.42%) 3.28s 3.39s
Total Time 11.16s (± 0.35%) 11.15s (± 0.45%) -0.01s (- 0.13%) 11.05s 11.25s
TFS - node (v14.15.1, x64)
Memory used 288,904k (± 0.01%) 288,897k (± 0.01%) -7k (- 0.00%) 288,803k 288,976k
Parse Time 1.31s (± 0.67%) 1.35s (± 1.98%) +0.03s (+ 2.51%) 1.29s 1.43s
Bind Time 0.73s (± 0.79%) 0.73s (± 0.61%) -0.00s (- 0.27%) 0.72s 0.74s
Check Time 5.09s (± 0.46%) 5.12s (± 0.32%) +0.03s (+ 0.59%) 5.09s 5.16s
Emit Time 3.48s (± 2.16%) 3.54s (± 1.92%) +0.06s (+ 1.78%) 3.37s 3.62s
Total Time 10.61s (± 0.96%) 10.73s (± 0.85%) +0.12s (+ 1.17%) 10.50s 10.85s
material-ui - node (v14.15.1, x64)
Memory used 453,642k (± 0.01%) 453,608k (± 0.01%) -33k (- 0.01%) 453,520k 453,706k
Parse Time 1.85s (± 0.40%) 1.85s (± 0.27%) +0.00s (+ 0.22%) 1.84s 1.86s
Bind Time 0.71s (± 1.06%) 0.71s (± 1.09%) +0.00s (+ 0.28%) 0.68s 0.72s
Check Time 20.41s (± 0.88%) 20.30s (± 0.48%) -0.11s (- 0.53%) 20.13s 20.55s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 22.96s (± 0.79%) 22.86s (± 0.42%) -0.10s (- 0.45%) 22.69s 23.09s
xstate - node (v14.15.1, x64)
Memory used 535,372k (± 0.01%) 535,307k (± 0.01%) -66k (- 0.01%) 535,254k 535,369k
Parse Time 2.57s (± 0.39%) 2.57s (± 0.38%) +0.00s (+ 0.04%) 2.54s 2.59s
Bind Time 1.15s (± 1.10%) 1.15s (± 0.83%) -0.00s (- 0.43%) 1.13s 1.17s
Check Time 1.51s (± 0.39%) 1.50s (± 0.43%) -0.00s (- 0.27%) 1.49s 1.51s
Emit Time 0.07s (± 3.14%) 0.07s (± 0.00%) -0.00s (- 1.41%) 0.07s 0.07s
Total Time 5.30s (± 0.35%) 5.29s (± 0.28%) -0.01s (- 0.17%) 5.24s 5.31s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory4 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v10.16.3, x64)
  • Compiler-Unions - node (v12.1.0, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v10.16.3, x64)
  • xstate - node (v12.1.0, x64)
  • xstate - node (v14.15.1, x64)
Benchmark Name Iterations
Current 48094 10
Baseline main 10

Developer Information:

Download Benchmark

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@rbuckton rbuckton force-pushed the templateLiteralTypeInferToNonString branch from 21c053c to 00c4b26 Compare May 10, 2022 17:56
@rbuckton
Copy link
Member Author

@ahejlsberg can you take another look?

@weswigham I put in a check that will hopefully work with #47050, or at least be a start.

@anuraghazra
Copy link

Will this also support floating point numbers? or just integers?

@somebody1234
Copy link

@reverofevil
Copy link

Oh, so we're not far from computing a sum of two numeric literal types!

type Test = Sum<1099123, 9901123>

type ToString<A extends number> = `${A}`
type Reverse<A extends string> =
    A extends `${infer H}${infer T}`
        ? `${Reverse<T>}${H}`
        : A
type HT<T> = T extends `${infer H}${infer T}` ? {H: H, T: T} : {H: '0', T: ''}
type Head<T> = T extends `${infer H}${any}` ? H : '0'
type Tail<T> = T extends `${string}${infer T}` ? T : ''
type R0 = Tail<''>
type Concat<A, B> = A extends string ? B extends string ? `${A}${B}` : never : never
type Nums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
type Shuffle<T extends any[]> = T extends [infer H, ...infer T] ? [...T, H] : never
type MakeTable<T extends any[], S extends any[]> = T extends [any, ...infer T] ? [S, ...MakeTable<T, Shuffle<S>>] : []
type SumTable = MakeTable<Nums, Nums>
type SumD = {0: SumTable, 1: Shuffle<SumTable>}
type Index<B, A> = A extends keyof B ? B[A] : never
type SumDR<A, B, C> = Index<Index<Index<SumD, A>, B>, C>
type Carries = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
type CarryTable = MakeTable<[0, ...Nums], Carries>
type SumC = {0: CarryTable, 1: Shuffle<CarryTable>}
type Lookup<T, C, A, B> = Index<Index<Index<T, C>, Head<A>>, Head<B>>
type SumS<A extends string, B extends string, C = 0> = Concat<A, B> extends ''
    ? C extends 1 ? '1' : ''
    : Concat<SumS<Tail<A>, Tail<B>, Lookup<SumC, C, A, B>>, Lookup<SumD, C, A, B>>
type Sum<A extends number, B extends number> = SumS<Reverse<ToString<A>>, Reverse<ToString<B>>>

@rbuckton rbuckton force-pushed the templateLiteralTypeInferToNonString branch from 80877c2 to 9ccca98 Compare May 24, 2022 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Feature request: Ability to parse a stringified number on type-level
9 participants