Skip to content

Fatal OOM crash on recursive template literal typeΒ #63197

@james-pre

Description

@james-pre

πŸ”Ž Search Terms

crash template

πŸ•— Version & Regression Information

This issue occurrs with 5.9.3, 6.0.0-beta, and results in a system OOM instead of V8 crash when using the latest tsgo.

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/PQKhFgCgAIWgxANgQwC6oKYDtrJwewCMArDAY1WgGdUAnAVwvto2gEstV9dqNL8AZtABEAawwBPKsIB0UWPLgABDAA9kAWwAOiDIugADI6ir7UErazWadrALwIU6bAGlJVADwBvaAAsAXNA+VIE+iIEAjADc0ABugQBM0AC+KSkAfFFmFqwsVPSIlA7CvsLQAD4ivjLSFVU1MohllSUNscJZMHBGBorAUGpa+LSU5paOaJhYblIeAPLp0A5eUACQANou7DjiEoLQc9AAZNAAFDS0HADmdVj0GoQYtACUALqBc5uv0GpTACZUaAAJXIwz+HjwEgANLgsBJ0mtVgB+aBbSoGAAkXhcyRkWKQk1c7nmX3SyV6kFWq0CLk6yXWh1+2AB0BYyD++CwiAksIk61eaxRu32hxOmK8dweT3Ja0CwqEorOF2ut3ujxer06A1UQxG0DGrBmAGUMFpkLQ0MMltAAOQyG11G3rB2VG2vG1ayCgCBdaAAcT4PFiyEQ9FY9CoKuQ0DNqF80H2Wlo+EsIx5uyoci6-Ugg2Goxy-r4ACEJEa6NcPGsACJoZBQtYABTQ8aZWBZyqwN0qkvV1oJzmmxNrqGQ6QbkEWDmbcZ+qn+gIMAH0l0n8FwV1jxcbTebLbRkljO93oL3pQZHTaZZSUVgMLEnrLoDPW-PmYusTuzRauAesRwBCeYEMBoa8qRRANUFLctLi7DwR3rYCaARSlqWfFs5wXQx-ywQDaCQ1BD2xSQTW-fcwPAosoLLCs4IQmEQWQxE0JfTD32wrwAKAmY2PbQF5WgBDjiVWiTzPF4iK-PdfyIrj8MYwiKSpSjIOg0T4LrTZJFeBiQNQFDlJYjC2xZAShJOc5RNVKUXmYyiEPWF8BVQ5TAnodsMAEDgMD+T0OEwWgBGQMhWGrRhRCCNZR1UTl8A0CRQkRK5sAjQIbTwZAqA9REqEsMg2BAtKdDQCRaF8CQsDIXx8CyzpVmSOkoCgA1BPCo08oKwEHFUmjYKueDwphG1oti+Kag6kCbUyaBgGAW1ivMMqKqqmqbSgIA

πŸ’» Code

This issue is minimally reproducible with a single line:

type Strip<T extends string> = T extends `${infer Rest}.` ? Strip<Rest> : T;

My use case is more complicated:

/**
 * Flatten an object structure into a set of "keys".
 *
 * @example
 * ```ts
 * type example = FlattenKeys<{ h: { s: { l: 1; v: 2 } } }>;
 * type result = "h" | "h.s" | "h.s.l" | "h.s.v";
 * ```
 */
export type FlattenKeys<O> = {
	[K in keyof O & (string | number)]: O[K] extends Record<any, any>
		? K | `${K}.${FlattenKeys<O[K]>}`
		: K;
}[O extends readonly any[]
	? keyof O & `${number}`
	: keyof O & (string | number)];

export type KeySeparator = '.' | '[' | ']';

/**
 * Get a value using a path of property keys.
 */
export type GetByString<
	Data,
	Path extends string | number = FlattenKeys<Data>,
> = Path extends `__proto__${`${KeySeparator}${string | number}` | ''}`
	? never
	: Path extends `${KeySeparator}${infer Rest}`
		? GetByString<Data, Rest>
		: Path extends `${infer Rest}${KeySeparator}`
			? GetByString<Data, Rest>
			: Path extends `${infer Key extends keyof Data & (string | number)}${KeySeparator}${infer Rest}`
				? GetByString<Data[Key], Rest>
				: Path extends keyof Data & (string | number)
					? Data[Path]
					: undefined;

interface Duck {
	taxonomy: {
		genus: 'anas';
		species: 'platyrhynchos';
	};
}

type DuckSpecies = GetByString<Duck, 'taxonomy.species'>; // 'platyrhynchos'

πŸ™ Actual behavior

V8 runs out of memory

$ npx tsc

<--- Last few GCs --->

[424790:0x56029fb82000]    22925 ms: Scavenge (interleaved) 4066.4 (4075.8) -> 4060.5 (4092.8) MB, pooled: 0 MB, 8.24 / 0.00 ms  (average mu = 0.375, current mu = 0.357) allocation failure; 
[424790:0x56029fb82000]    23581 ms: Mark-Compact (reduce) 4060.6 (4092.8) -> 4060.3 (4063.0) MB, pooled: 0 MB, 510.79 / 0.00 ms  (+ 7.7 ms in 2 steps since start of marking, biggest step 5.0 ms, walltime since start of marking 527 ms) (average mu = 0.354
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

 1: 0x7f9d11a42aaf node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/lib64/libnode.so.137]
 2: 0x7f9d12e5e1f4  [/lib64/libnode.so.137]
 3: 0x7f9d12e5e2f6  [/lib64/libnode.so.137]
 4: 0x7f9d1314a77b  [/lib64/libnode.so.137]
 5: 0x7f9d1314a7ab  [/lib64/libnode.so.137]
 6: 0x7f9d1314aa4f  [/lib64/libnode.so.137]
 7: 0x7f9d131609ec  [/lib64/libnode.so.137]
 8: 0x7f9d13164e14  [/lib64/libnode.so.137]
 9: 0x7f9d13db44f7  [/lib64/libnode.so.137]

πŸ™‚ Expected behavior

It works, or better yet gives a TSC error.

Additional information about the issue

While the single-line example is undoubtedly poor code, I think it should never cause a system OOM or the compiler to crash like this.

In fact, tsgo's behavior is more troubling and could result in a DOS.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions