TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

More specific TemplateStringsArray type for tagged templates

Open rbuckton opened this issue 2 years ago • 20 comments

This introduces a more specific type over TemplateStingsArray when invoking a tagged template. This would then allow developers to interpolate the literal types provided to the tagged template invocation, along with the provided arguments:


type Interpolate<T extends readonly string[], A extends any[], R extends string = ''> =
    T extends readonly [infer TH extends string, ...infer TT extends readonly string[]] ?
        A extends [infer AH extends string, ...infer AT extends string[]] ?
            Interpolate<TT, AT, `${R}${TH}${AH}`> :
            Interpolate<TT, [], `${R}${TH}`> :
        R;

declare function interp<
  T extends TemplateStringsArray,
  A extends string[]
>(strs: T, ...args: A): Interpolate<T, A>;

const a = interp`a${"b"}c`; // "abc"
const b = interp`1${2}3`; // "123"

The type we manufacture for the tagged template invocation is an instantiation of TemplateStringsArrayOf<Cooked, Raw>:

type TemplateStringsArrayOf<
  Cooked extends readonly string[],
  Raw extends readonly string[]
> =
  Cooked & { readonly raw: Raw };

This would then allow you to perform custom interpolation against either the "cooked" values (where escapes are normalized), and "raw" values (where escapes are preserved):

declare function raw<
  T extends TemplateStringsArray,
  A extends string[]
>(strs: T, ...args: A): Interpolate<T["raw"], A>;

const a = raw`a\n${"b"}\nc`; // "a\\nb\\nc"

In theory, more complex interpolation could also be achieved, but that is an exercise left to the reader:

type QueryResult<T extends readonly string[], A extends any[]> =
   // some type that parses T and interpolates A...
  todo;

declare function query<T extends TemplateStringsArray, A extends any[]>(strs: T, ...args: A): QueryResult<T, A>;

interface Person { firstName: string, lastName: string }
declare const people: Iterable<Person>;

const result = query`
  for x in ${people}
  where x.firstName === ${"Bob"}
  select x.lastName
`;

result; // Iterable<string>

To support interpolation over a TemplateStringsArray, I also needed to make a change to inferFromObjectTypes. Prior to this change you could not infer to tuple element positions if the source type was an intersection of a tuple and another type:

type X<T extends any[]> = T extends [infer H, ...infer _] ? H : never;

// before
type T1 = X<[1, 2]>; // 1 (ok)
type T2 = X<[1, 2] & { foo: true }>; // never (😞)

However, this becomes a problem when trying to pick apart a generic TemplateStringsArrayOf type. To address this, this PR makes a small change in how we perform this inference. Nothing changes when a source type is already an array or tuple type, but if the source type is instead an intersection of an array or tuple type and another type, we will also now permit tuple element inference as long as the intersected types do not shadow tuple-specific properties (i.e., numeric indexers, numeric-stringed properties, or the "length" property):

type X<T extends any[]> = T extends [infer H, ...infer _] ? H : never;

// after
type T1 = X<[1, 2]>; // 1 (ok)
type T2 = X<[1, 2] & { foo: true }>; // 1 (ok)

Fixes #33304 Fixes #31422 Related #29432 Related #16551

rbuckton avatar Jun 15 '22 02:06 rbuckton

I tinkered with an example of a tail-recursive parser for jest.each here, though it can probably be drastically simplified.

rbuckton avatar Jun 16 '22 02:06 rbuckton

Here's a much simpler version of Jest's each:

type IDStart =
    | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
    | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
    | "$" | "_"
    ;

type IDPart =
    | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
    | IDStart
    ;

type Trim<S extends string, Chars extends string = " " | "\t" | "\v" | "\n"> =
    S extends (Chars extends "" ? S : never) ? S :
    S extends `${Chars}${infer R}` ? Trim<R, Chars> :
    S extends `${infer R}${Chars}` ? Trim<R, Chars> :
    S;

type ParseHeaders<S extends string> =
    S extends "|" ? [] :
    S extends `${infer H}|${infer T}` ? [Trim<H>, ...ParseHeaders<T>] :
    [Trim<S>];

type CheckHeader<TIn extends string, TWork extends string = TIn, Valid extends IDPart = IDStart> =
    TWork extends `${infer Ch}${infer R}` ?
        Ch extends Valid ? CheckHeader<TIn, R, IDPart> :
            JestEachParseError<`Invalid identifier: '${TIn}'`> :
    TIn extends '' ? JestEachParseError<`Identifier expected.`> :
        TIn;

type CheckHeaders<TIn extends string[], TWork extends string[] = TIn> =
    TWork extends [infer TH extends string, ...infer TT extends string[]] ?
        CheckHeader<TH> extends infer E extends JestEachParseError<any> ? E :
            CheckHeaders<TIn, TT> :
        TIn;

type ParseRows<A extends any[], S extends readonly string[], Row extends any[] = [], Rows extends any[][] = []> =
    [A, S] extends [[infer AH, ...infer AT], readonly [infer TH extends string, ...infer TT extends string[]]] ?
        Trim<TH, " " | "\t" | "\v"> extends "|" ? ParseRows<AT, TT, [...Row, AH], Rows> :
        Trim<TH, " " | "\t" | "\v"> extends "\n" | "" ? ParseRows<AT, TT, [], [...Rows, [...Row, AH]]> :
            JestEachParseError<`Unexpected character: '${Trim<TH>}'`> :
    [A, S] extends [[], readonly []] ? Rows :
        JestEachParseError<`Mismatched elements`>;

type JestEachArgument<Headers extends string[] | JestEachParseError<any>, Rows extends any[][] | JestEachParseError<any>> =
    Headers extends string[] ?
        Rows extends any[][] ?
            {
                [P1 in keyof Rows]: {
                    [P2 in keyof Headers as P2 extends `${number}` ? Headers[P2] : never]:
                        P2 extends keyof Rows[P1] ? Rows[P1][P2] : undefined;
                };
            }[number] :
            Rows :
        Headers;

type JestEachFunction<Arg> =
    Arg extends JestEachParseError<any> ? Arg :
    (name: string, cb: (arg: Arg) => void, timeout?: number) => void;

declare const JestEachParseError: unique symbol;

interface JestEachParseError<Message extends string> {
    [JestEachParseError]: Message;
}

type JestEach<T extends readonly string[], A extends any[]> =
    T extends readonly [infer TH extends string, ...infer TT extends readonly string[]] ?
        JestEachFunction<JestEachArgument<CheckHeaders<ParseHeaders<TH>>, ParseRows<A, TT>>> :
        JestEachParseError<`Mismatched elements`>;

declare function each<T extends readonly string[], A extends (string | symbol | number | bigint | boolean | null | undefined | object)[]>(strs: T, ...args: A): JestEach<T, A>;

// jest
it.each`
    foo    | bar
    ${"a"} | ${"b"}
    ${"a"} | ${"c"}
`("test", ({ foo, bar }) => {
//           ^? any
//                ^? any
});

each`
    foo    | bar
    ${"a"} | ${"b"}
    ${"c"} | ${"d"}
`("test", ({ foo, bar }) => {
//           ^? "a" | "c"
//                ^? "b" | "d"
});

rbuckton avatar Jun 16 '22 22:06 rbuckton

There is no longer any order dependence. The behavior for inferring from an intersection of two tuple types now falls back to pre-existing behavior.

rbuckton avatar Jun 17 '22 00:06 rbuckton

Also, I've added a test for Jest's each based on the example above.

rbuckton avatar Jun 17 '22 00:06 rbuckton

@DanielRosenwasser: Are we comfortable taking this for 4.8 or would you rather wait?

rbuckton avatar Jun 17 '22 01:06 rbuckton

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

rbuckton avatar Jun 17 '22 01:06 rbuckton

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

typescript-bot avatar Jun 17 '22 01:06 typescript-bot

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

typescript-bot avatar Jun 17 '22 01:06 typescript-bot

Heya @rbuckton, I've started to run the diff-based user code test suite on this PR at f8752ad296573db618861f4c4b75b60682bee682. You can monitor the build here.

Update: The results are in!

typescript-bot avatar Jun 17 '22 01:06 typescript-bot

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

typescript-bot avatar Jun 17 '22 01:06 typescript-bot

Heya @rbuckton, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.

typescript-bot avatar Jun 17 '22 02:06 typescript-bot

@rbuckton Great news! no new errors were found between main..refs/pull/49552/merge

typescript-bot avatar Jun 17 '22 02:06 typescript-bot

@typescript-bot perf test

rbuckton avatar Jun 17 '22 18:06 rbuckton

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

Update: The results are in!

typescript-bot avatar Jun 17 '22 18:06 typescript-bot

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

Here they are:

Comparison Report - main..49552

Metric main 49552 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 359,557k (± 0.02%) 359,644k (± 0.02%) +87k (+ 0.02%) 359,533k 359,779k
Parse Time 2.11s (± 0.63%) 2.11s (± 0.41%) -0.00s (- 0.05%) 2.10s 2.14s
Bind Time 0.91s (± 1.05%) 0.90s (± 0.78%) -0.00s (- 0.44%) 0.89s 0.92s
Check Time 5.95s (± 0.43%) 5.99s (± 0.29%) +0.04s (+ 0.71%) 5.96s 6.04s
Emit Time 6.11s (± 0.77%) 6.14s (± 0.65%) +0.03s (+ 0.49%) 6.08s 6.25s
Total Time 15.07s (± 0.45%) 15.14s (± 0.27%) +0.07s (+ 0.47%) 15.07s 15.24s
Compiler-Unions - node (v10.16.3, x64)
Memory used 206,384k (± 0.04%) 206,445k (± 0.05%) +61k (+ 0.03%) 206,241k 206,661k
Parse Time 0.84s (± 0.89%) 0.84s (± 1.19%) +0.00s (+ 0.00%) 0.82s 0.87s
Bind Time 0.53s (± 1.59%) 0.53s (± 0.84%) +0.00s (+ 0.19%) 0.52s 0.54s
Check Time 8.12s (± 1.07%) 8.18s (± 0.52%) +0.06s (+ 0.73%) 8.09s 8.26s
Emit Time 2.51s (± 1.05%) 2.50s (± 0.69%) -0.00s (- 0.08%) 2.47s 2.54s
Total Time 11.99s (± 0.85%) 12.05s (± 0.44%) +0.06s (+ 0.48%) 11.95s 12.18s
Monaco - node (v10.16.3, x64)
Memory used 343,744k (± 0.02%) 343,785k (± 0.01%) +41k (+ 0.01%) 343,720k 343,859k
Parse Time 1.59s (± 0.76%) 1.59s (± 0.76%) +0.01s (+ 0.50%) 1.56s 1.62s
Bind Time 0.77s (± 1.01%) 0.77s (± 0.67%) 0.00s ( 0.00%) 0.76s 0.78s
Check Time 5.98s (± 0.64%) 5.95s (± 0.44%) -0.03s (- 0.45%) 5.89s 6.00s
Emit Time 3.24s (± 0.85%) 3.26s (± 0.73%) +0.02s (+ 0.77%) 3.21s 3.34s
Total Time 11.57s (± 0.54%) 11.58s (± 0.26%) +0.00s (+ 0.03%) 11.50s 11.64s
TFS - node (v10.16.3, x64)
Memory used 305,120k (± 0.02%) 305,158k (± 0.03%) +38k (+ 0.01%) 304,964k 305,420k
Parse Time 1.29s (± 0.78%) 1.29s (± 0.53%) +0.00s (+ 0.00%) 1.28s 1.31s
Bind Time 0.73s (± 0.92%) 0.73s (± 0.65%) +0.00s (+ 0.00%) 0.72s 0.74s
Check Time 5.41s (± 0.59%) 5.38s (± 0.60%) -0.02s (- 0.44%) 5.31s 5.45s
Emit Time 3.40s (± 1.03%) 3.44s (± 0.68%) +0.04s (+ 1.12%) 3.39s 3.50s
Total Time 10.83s (± 0.53%) 10.84s (± 0.28%) +0.01s (+ 0.12%) 10.77s 10.91s
material-ui - node (v10.16.3, x64)
Memory used 469,379k (± 0.01%) 471,939k (± 0.01%) +2,560k (+ 0.55%) 471,751k 472,084k
Parse Time 1.85s (± 0.77%) 1.84s (± 0.58%) -0.00s (- 0.22%) 1.82s 1.87s
Bind Time 0.69s (± 1.34%) 0.69s (± 1.34%) +0.00s (+ 0.00%) 0.67s 0.71s
Check Time 14.51s (± 0.73%) 14.62s (± 0.71%) +0.12s (+ 0.82%) 14.40s 14.85s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 17.04s (± 0.66%) 17.16s (± 0.58%) +0.12s (+ 0.68%) 16.94s 17.36s
xstate - node (v10.16.3, x64)
Memory used 584,715k (± 1.67%) 578,260k (± 0.02%) -6,455k (- 1.10%) 578,002k 578,446k
Parse Time 2.64s (± 0.40%) 2.63s (± 0.44%) -0.01s (- 0.27%) 2.61s 2.66s
Bind Time 1.05s (± 0.96%) 1.04s (± 0.97%) -0.00s (- 0.10%) 1.03s 1.07s
Check Time 1.55s (± 0.65%) 1.55s (± 0.62%) +0.00s (+ 0.19%) 1.52s 1.56s
Emit Time 0.07s (± 3.14%) 0.07s (± 0.00%) -0.00s (- 1.41%) 0.07s 0.07s
Total Time 5.29s (± 0.36%) 5.29s (± 0.26%) -0.01s (- 0.13%) 5.25s 5.31s
Angular - node (v12.1.0, x64)
Memory used 337,240k (± 0.02%) 337,315k (± 0.02%) +75k (+ 0.02%) 337,212k 337,429k
Parse Time 2.10s (± 0.66%) 2.10s (± 0.59%) +0.00s (+ 0.10%) 2.07s 2.13s
Bind Time 0.86s (± 1.16%) 0.86s (± 1.27%) +0.00s (+ 0.23%) 0.84s 0.90s
Check Time 5.76s (± 0.60%) 5.77s (± 0.56%) +0.01s (+ 0.21%) 5.70s 5.83s
Emit Time 6.36s (± 0.68%) 6.38s (± 0.68%) +0.02s (+ 0.31%) 6.25s 6.49s
Total Time 15.08s (± 0.30%) 15.11s (± 0.45%) +0.04s (+ 0.25%) 14.96s 15.26s
Compiler-Unions - node (v12.1.0, x64)
Memory used 194,005k (± 0.14%) 193,964k (± 0.16%) -41k (- 0.02%) 193,010k 194,297k
Parse Time 0.83s (± 1.21%) 0.84s (± 1.50%) +0.01s (+ 1.09%) 0.81s 0.86s
Bind Time 0.54s (± 0.67%) 0.55s (± 0.62%) +0.00s (+ 0.55%) 0.54s 0.55s
Check Time 7.63s (± 0.92%) 7.58s (± 0.78%) -0.05s (- 0.68%) 7.48s 7.72s
Emit Time 2.51s (± 0.78%) 2.54s (± 0.96%) +0.03s (+ 1.15%) 2.48s 2.61s
Total Time 11.51s (± 0.68%) 11.50s (± 0.72%) -0.02s (- 0.15%) 11.37s 11.74s
Monaco - node (v12.1.0, x64)
Memory used 326,866k (± 0.02%) 326,850k (± 0.02%) -16k (- 0.00%) 326,646k 326,984k
Parse Time 1.56s (± 0.69%) 1.56s (± 1.00%) -0.00s (- 0.26%) 1.53s 1.59s
Bind Time 0.76s (± 1.02%) 0.76s (± 0.45%) -0.00s (- 0.53%) 0.75s 0.76s
Check Time 5.76s (± 0.66%) 5.78s (± 0.39%) +0.01s (+ 0.23%) 5.71s 5.81s
Emit Time 3.28s (± 0.48%) 3.28s (± 0.52%) -0.00s (- 0.12%) 3.24s 3.31s
Total Time 11.37s (± 0.49%) 11.37s (± 0.41%) +0.00s (+ 0.03%) 11.25s 11.46s
TFS - node (v12.1.0, x64)
Memory used 289,745k (± 0.02%) 289,675k (± 0.07%) -70k (- 0.02%) 288,847k 289,941k
Parse Time 1.31s (± 1.22%) 1.29s (± 0.74%) -0.01s (- 0.84%) 1.28s 1.32s
Bind Time 0.75s (± 1.01%) 0.76s (± 0.65%) +0.00s (+ 0.13%) 0.75s 0.77s
Check Time 5.33s (± 0.62%) 5.32s (± 0.52%) -0.01s (- 0.15%) 5.24s 5.39s
Emit Time 3.50s (± 0.68%) 3.48s (± 0.76%) -0.02s (- 0.63%) 3.43s 3.54s
Total Time 10.88s (± 0.50%) 10.85s (± 0.36%) -0.03s (- 0.30%) 10.77s 10.95s
material-ui - node (v12.1.0, x64)
Memory used 448,352k (± 0.02%) 450,817k (± 0.06%) +2,466k (+ 0.55%) 449,731k 451,099k
Parse Time 1.85s (± 0.44%) 1.84s (± 0.56%) -0.01s (- 0.59%) 1.82s 1.87s
Bind Time 0.68s (± 0.98%) 0.68s (± 0.65%) -0.00s (- 0.15%) 0.67s 0.69s
Check Time 13.10s (± 0.73%) 13.10s (± 0.62%) -0.00s (- 0.03%) 12.94s 13.27s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.64s (± 0.64%) 15.62s (± 0.53%) -0.02s (- 0.10%) 15.44s 15.79s
xstate - node (v12.1.0, x64)
Memory used 546,965k (± 1.32%) 546,968k (± 1.31%) +3k (+ 0.00%) 543,492k 575,977k
Parse Time 2.59s (± 0.38%) 2.58s (± 0.50%) -0.01s (- 0.50%) 2.55s 2.60s
Bind Time 1.04s (± 1.11%) 1.03s (± 1.03%) -0.00s (- 0.29%) 1.01s 1.06s
Check Time 1.50s (± 0.83%) 1.49s (± 0.78%) -0.00s (- 0.20%) 1.47s 1.52s
Emit Time 0.07s (± 0.00%) 0.07s (± 0.00%) 0.00s ( 0.00%) 0.07s 0.07s
Total Time 5.20s (± 0.32%) 5.17s (± 0.51%) -0.02s (- 0.44%) 5.11s 5.22s
Angular - node (v14.15.1, x64)
Memory used 335,338k (± 0.01%) 335,372k (± 0.01%) +35k (+ 0.01%) 335,286k 335,431k
Parse Time 2.08s (± 0.63%) 2.07s (± 0.69%) -0.01s (- 0.38%) 2.04s 2.11s
Bind Time 0.90s (± 0.84%) 0.91s (± 1.18%) +0.01s (+ 0.77%) 0.90s 0.95s
Check Time 5.74s (± 0.37%) 5.76s (± 0.44%) +0.02s (+ 0.35%) 5.67s 5.79s
Emit Time 6.40s (± 0.61%) 6.42s (± 0.88%) +0.02s (+ 0.38%) 6.31s 6.58s
Total Time 15.12s (± 0.32%) 15.16s (± 0.47%) +0.04s (+ 0.26%) 15.02s 15.35s
Compiler-Unions - node (v14.15.1, x64)
Memory used 192,627k (± 0.02%) 192,648k (± 0.01%) +21k (+ 0.01%) 192,598k 192,702k
Parse Time 0.84s (± 0.87%) 0.84s (± 0.90%) +0.01s (+ 0.84%) 0.83s 0.87s
Bind Time 0.58s (± 0.69%) 0.58s (± 1.01%) -0.00s (- 0.69%) 0.57s 0.59s
Check Time 7.66s (± 0.60%) 7.68s (± 0.65%) +0.02s (+ 0.30%) 7.56s 7.77s
Emit Time 2.50s (± 0.59%) 2.51s (± 0.43%) +0.01s (+ 0.44%) 2.49s 2.54s
Total Time 11.58s (± 0.44%) 11.61s (± 0.41%) +0.04s (+ 0.33%) 11.49s 11.68s
Monaco - node (v14.15.1, x64)
Memory used 325,577k (± 0.01%) 325,588k (± 0.01%) +11k (+ 0.00%) 325,537k 325,638k
Parse Time 1.57s (± 0.47%) 1.57s (± 0.60%) -0.00s (- 0.19%) 1.56s 1.60s
Bind Time 0.79s (± 0.98%) 0.79s (± 0.89%) +0.00s (+ 0.25%) 0.78s 0.81s
Check Time 5.66s (± 0.41%) 5.67s (± 0.71%) +0.01s (+ 0.11%) 5.61s 5.76s
Emit Time 3.36s (± 0.75%) 3.35s (± 0.65%) -0.01s (- 0.36%) 3.30s 3.38s
Total Time 11.38s (± 0.37%) 11.38s (± 0.55%) -0.01s (- 0.05%) 11.27s 11.54s
TFS - node (v14.15.1, x64)
Memory used 288,764k (± 0.01%) 288,771k (± 0.01%) +8k (+ 0.00%) 288,720k 288,851k
Parse Time 1.32s (± 1.02%) 1.32s (± 1.28%) +0.00s (+ 0.23%) 1.29s 1.36s
Bind Time 0.76s (± 0.91%) 0.76s (± 1.41%) +0.00s (+ 0.53%) 0.74s 0.79s
Check Time 5.32s (± 0.48%) 5.32s (± 0.64%) -0.00s (- 0.02%) 5.26s 5.41s
Emit Time 3.64s (± 1.91%) 3.62s (± 1.69%) -0.01s (- 0.41%) 3.46s 3.68s
Total Time 11.03s (± 0.72%) 11.02s (± 0.72%) -0.00s (- 0.05%) 10.79s 11.13s
material-ui - node (v14.15.1, x64)
Memory used 446,630k (± 0.00%) 449,208k (± 0.00%) +2,578k (+ 0.58%) 449,175k 449,237k
Parse Time 1.90s (± 0.50%) 1.89s (± 0.44%) -0.02s (- 1.00%) 1.87s 1.90s
Bind Time 0.73s (± 1.27%) 0.73s (± 0.88%) +0.00s (+ 0.55%) 0.71s 0.74s
Check Time 13.25s (± 0.92%) 13.26s (± 0.72%) +0.01s (+ 0.10%) 13.04s 13.51s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.88s (± 0.80%) 15.88s (± 0.60%) -0.00s (- 0.00%) 15.67s 16.13s
xstate - node (v14.15.1, x64)
Memory used 541,401k (± 0.00%) 541,456k (± 0.00%) +55k (+ 0.01%) 541,393k 541,500k
Parse Time 2.62s (± 0.52%) 2.62s (± 0.32%) -0.00s (- 0.15%) 2.60s 2.64s
Bind Time 1.18s (± 0.80%) 1.18s (± 0.92%) +0.01s (+ 0.42%) 1.15s 1.20s
Check Time 1.53s (± 0.48%) 1.54s (± 0.31%) +0.01s (+ 0.59%) 1.53s 1.55s
Emit Time 0.07s (± 3.14%) 0.07s (± 4.66%) +0.00s (+ 2.82%) 0.07s 0.08s
Total Time 5.41s (± 0.32%) 5.41s (± 0.36%) +0.00s (+ 0.07%) 5.36s 5.45s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory1 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 49552 10
Baseline main 10
Developer Information:

Download Benchmark

typescript-bot avatar Jun 17 '22 19:06 typescript-bot

This is a feature I've been waiting for a long time, but I forgot why. 😄

MartinJohns avatar Jun 28 '22 08:06 MartinJohns

Hi, this PR looks fantastic (I've been hoping for this feature for a long time)! Wondering what the status is?

harrysolovay avatar Aug 12 '22 04:08 harrysolovay

Is there any progress for this feature?

SieR-VR avatar Oct 28 '22 09:10 SieR-VR

I hope the pending reviews can be done soon so we will see this released soon. This would open the door to a lot of interesting things.

mzinner avatar Nov 03 '22 16:11 mzinner

This PR is currently on hold. The main use cases for this feature require a fair amount of complexity that is likely to run any implementation against the recursion depth limiter. Such types are expensive to compute and check, and often result in degraded performance in an editor. It's unlikely that we will ship a feature that introduces this much complexity without first making significant improvements in overall type checking performance.

I do plan to keep this PR alive and will periodically update it, but I do not foresee it shipping in the near future.

rbuckton avatar Nov 04 '22 21:11 rbuckton

There are also use cases which require no recursions. One example is the generation of GraphQL types from .ts files as described here:
https://the-guild.dev/graphql/codegen/plugins/presets/gql-tag-operations-preset

The idea is to generate return types for queries based on your written code. For example:

import { gql } from "gql.generated"; // <--- gqls types are generated from all .ts files
const userQuery = const USER_QUERY = gql(`
  query User($id: String!) {
    user(id: $id) {
      id
      name
    }
  }
`);
execute(userQuery, "12") // <--- typed - will return Promise<{user: { id: string, name: string } }>

However for graphql fragments we would need to use template literals to merge the return types of the query and fragments:

import { gql } from "gql.generated";
const fragment = gql`a fragment query`;
const userQuery = gql`a graphql query { ${fragment} }`

jantimon avatar Nov 10 '22 14:11 jantimon

Another year has passed, and version 5.x has now been released. This version has optimized the performance of comparison. Is it possible to consider introducing this feature in the new version? This helps us a lot, thank you very much for your work.

NWYLZW avatar Dec 27 '23 17:12 NWYLZW

i wrote a working type level parser for my library's html template string DSL so it can be typesafe but it's useless without this PR. JSX isn't an option due to lack of ability to cache static parts with default tooling. i'd love to see this merged.

easrng avatar Feb 26 '24 02:02 easrng

@rbuckton Any updates? Aren't v5's perf enhancements sufficient now?

aradalvand avatar Feb 29 '24 03:02 aradalvand

@typescript-bot pack this

rbuckton avatar Mar 08 '24 18:03 rbuckton

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started

typescript-bot avatar Mar 08 '24 18:03 typescript-bot

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/160237/artifacts?artifactName=tgz&fileId=C4F63B683D4A1D20951F7242BAF3E88934805D10EB48C11D866934D3E86690CF02&fileName=/typescript-4.8.0-insiders.20240308.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;

typescript-bot avatar Mar 08 '24 18:03 typescript-bot

@rbuckton Any updates? Aren't v5's perf enhancements sufficient now?

No, unfortunately. The performance concerns we have are related to the complex conditional types and inference that are necessary to "parse" a string literal type, and that still isn't very efficient at the moment.

rbuckton avatar Mar 08 '24 21:03 rbuckton

@rbuckton Has ArkType not proven it can be done efficiently at this point?

Even with the overhead of type-level string parsing (including syntactic and semantic validation), types are often 3-10x less expensive than equivalent Zod.

It would be a shame not to ship what would be an awesome feature just because it is possible to use inefficiently.

ssalbdivad avatar Mar 08 '24 23:03 ssalbdivad