From ed2be496022a51b8a2a34d0b7b6210b35493e7a1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 2 Mar 2022 14:25:20 -0800 Subject: [PATCH] More specific inference for constrained 'infer' types in template literal types --- src/compiler/checker.ts | 87 +++- .../templateLiteralTypes4.errors.txt | 107 +++++ .../reference/templateLiteralTypes4.js | 182 ++++++++ .../reference/templateLiteralTypes4.symbols | 402 ++++++++++++++++++ .../reference/templateLiteralTypes4.types | 244 +++++++++++ .../types/literal/templateLiteralTypes4.ts | 100 +++++ 6 files changed, 1116 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/templateLiteralTypes4.errors.txt create mode 100644 tests/baselines/reference/templateLiteralTypes4.js create mode 100644 tests/baselines/reference/templateLiteralTypes4.symbols create mode 100644 tests/baselines/reference/templateLiteralTypes4.types create mode 100644 tests/cases/conformance/types/literal/templateLiteralTypes4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 11acc4f269e..4b05503907a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21762,13 +21762,31 @@ namespace ts { sourceEnd.slice(sourceEnd.length - endLen) !== targetEnd.slice(targetEnd.length - endLen); } - function isValidBigIntString(s: string): boolean { + /** + * Tests whether the provided string can be parsed as a number. + * @param s The string to test. + * @param roundTripOnly Indicates the resulting number matches the input when converted back to a string. + */ + function isValidNumberString(s: string, roundTripOnly: boolean): boolean { + if (s === "") return false; + const n = +s; + return isFinite(n) && (!roundTripOnly || "" + n === s); + } + + /** + * Tests whether the provided string can be parsed as a bigint. + * @param s The string to test. + * @param roundTripOnly Indicates the resulting bigint matches the input when converted back to a string. + */ + function isValidBigIntString(s: string, roundTripOnly: boolean): boolean { + if (s === "") return false; const scanner = createScanner(ScriptTarget.ESNext, /*skipTrivia*/ false); let success = true; scanner.setOnError(() => success = false); scanner.setText(s + "n"); let result = scanner.scan(); - if (result === SyntaxKind.MinusToken) { + const negative = result === SyntaxKind.MinusToken; + if (negative) { result = scanner.scan(); } const flags = scanner.getTokenFlags(); @@ -21777,7 +21795,8 @@ namespace ts { // * a bigint can be scanned, and that when it is scanned, it is // * the full length of the input string (so the scanner is one character beyond the augmented input length) // * it does not contain a numeric seperator (the `BigInt` constructor does not accept a numeric seperator in its input) - return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator); + return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator) + && (!roundTripOnly || s === pseudoBigIntToString({ negative, base10Value: parsePseudoBigInt(scanner.getTokenValue()) })); } function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean { @@ -21786,8 +21805,8 @@ namespace ts { } if (source.flags & TypeFlags.StringLiteral) { const value = (source as StringLiteralType).value; - return !!(target.flags & TypeFlags.Number && value !== "" && isFinite(+value) || - target.flags & TypeFlags.BigInt && value !== "" && isValidBigIntString(value) || + return !!(target.flags & TypeFlags.Number && isValidNumberString(value, /*roundTripOnly*/ false) || + target.flags & TypeFlags.BigInt && isValidBigIntString(value, /*roundTripOnly*/ false) || target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (target as IntrinsicType).intrinsicName); } if (source.flags & TypeFlags.TemplateLiteral) { @@ -22365,7 +22384,63 @@ namespace ts { // succeed. That would be a pointless and confusing outcome. if (matches || every(target.texts, s => s.length === 0)) { for (let i = 0; i < types.length; i++) { - inferFromTypes(matches ? matches[i] : neverType, types[i]); + const source = matches ? matches[i] : neverType; + const target = types[i]; + + // If we are inferring from a string literal type to a type variable whose constraint includes one of the + // allowed template literal placeholder types, infer from a literal type corresponding to the constraint. + let sourceTypes: Type[] | undefined; + if (source.flags & TypeFlags.StringLiteral && target.flags & TypeFlags.TypeVariable) { + const inferenceContext = getInferenceInfoForType(target); + const constraint = inferenceContext ? getConstraintOfTypeParameter(inferenceContext.typeParameter) : undefined; + if (inferenceContext && constraint) { + const str = (source as StringLiteralType).value; + const constraintTypes = constraint.flags & TypeFlags.Union ? (constraint as UnionType).types : [constraint]; + for (const constraintType of constraintTypes) { + if (constraintType.flags & TypeFlags.StringLike) { + sourceTypes ??= []; + sourceTypes.push(source); + } + if (constraintType.flags & TypeFlags.NumberLike && isValidNumberString(str, /*roundTripOnly*/ true)) { + sourceTypes ??= []; + sourceTypes.push(getNumberLiteralType(+str)); + } + if (constraintType.flags & TypeFlags.BigIntLike && isValidBigIntString(str, /*roundTripOnly*/ true)) { + const negative = str.startsWith("-"); + const base10Value = parsePseudoBigInt(`${negative ? str.slice(1) : str}n`); + sourceTypes ??= []; + sourceTypes.push(getBigIntLiteralType({ negative, base10Value })); + } + if (constraintType.flags & TypeFlags.BooleanLike) { + if (str === trueType.intrinsicName) { + sourceTypes ??= []; + sourceTypes.push(trueType); + } + else if (str === falseType.intrinsicName) { + sourceTypes ??= []; + sourceTypes.push(falseType); + } + } + if (constraintType.flags & TypeFlags.Null && str === nullType.intrinsicName) { + sourceTypes ??= []; + sourceTypes.push(nullType); + } + if (constraintType.flags & TypeFlags.Undefined && str === undefinedType.intrinsicName) { + sourceTypes ??= []; + sourceTypes.push(undefinedType); + } + } + } + } + + if (sourceTypes) { + for (const source of sourceTypes) { + inferFromTypes(source, target); + } + } + else { + inferFromTypes(source, target); + } } } } diff --git a/tests/baselines/reference/templateLiteralTypes4.errors.txt b/tests/baselines/reference/templateLiteralTypes4.errors.txt new file mode 100644 index 00000000000..e92d5cafb6d --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes4.errors.txt @@ -0,0 +1,107 @@ +tests/cases/conformance/types/literal/templateLiteralTypes4.ts(93,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. +tests/cases/conformance/types/literal/templateLiteralTypes4.ts(97,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. + + +==== tests/cases/conformance/types/literal/templateLiteralTypes4.ts (2 errors) ==== + type Is = T; + + type T0 = "100" extends `${Is}` ? N : never; // 100 + type T1 = "-100" extends `${Is}` ? N : never; // -100 + type T2 = "1.1" extends `${Is}` ? N : never; // 1.1 + type T3 = "8e-11" extends `${Is}` ? N : never; // 8e-11 (0.00000000008) + type T4 = "0x10" extends `${Is}` ? N : never; // number (not round-trippable) + type T5 = "0o10" extends `${Is}` ? N : never; // number (not round-trippable) + type T6 = "0b10" extends `${Is}` ? N : never; // number (not round-trippable) + type T7 = "10e2" extends `${Is}` ? N : never; // number (not round-trippable) + type T8 = "abcd" extends `${Is}` ? N : never; // never + + type T10 = "100" extends `${Is}` ? N : never; // 100n + type T11 = "-100" extends `${Is}` ? N : never; // -100n + type T12 = "0x10" extends `${Is}` ? N : never; // bigint (not round-trippable) + type T13 = "0o10" extends `${Is}` ? N : never; // bigint (not round-trippable) + type T14 = "0b10" extends `${Is}` ? N : never; // bigint (not round-trippable) + type T15 = "1.1" extends `${Is}` ? N : never; // never + type T16 = "10e2" extends `${Is}` ? N : never; // never + type T17 = "abcd" extends `${Is}` ? N : never; // never + + type T20 = "true" extends `${Is}` ? T : never; // true + type T21 = "false" extends `${Is}` ? T : never; // false + type T22 = "abcd" extends `${Is}` ? T : never; // never + + type T30 = "null" extends `${Is}` ? T : never; // null + type T31 = "abcd" extends `${Is}` ? T : never; // never + + type T40 = "undefined" extends `${Is}` ? T : never; // undefined + type T41 = "abcd" extends `${Is}` ? T : never; // never + + type T50 = "100" extends `${Is}` ? T : never; // "100" | 100 | 100n + type T51 = "1.1" extends `${Is}` ? T : never; // "100" | 1.1 + type T52 = "true" extends `${Is}` ? T : never; // "true" | true + type T53 = "false" extends `${Is}` ? T : never; // "false" | false + type T54 = "null" extends `${Is}` ? T : never; // "null" | null + type T55 = "undefined" extends `${Is}` ? T : never; // "undefined" | undefined + + type NumberFor = S extends `${Is}` ? N : never; + type T60 = NumberFor<"100">; // 100 + type T61 = NumberFor; // never + type T62 = NumberFor; // never + + // example use case: + interface FieldDefinition { + readonly name: string; + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; + } + + type FieldType = + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : + T extends "f32" | "f64" ? bigint : + never; + + // Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` + type TypedObjectNamedMembers = { + [P in TDef[number]["name"]]: FieldType["type"]>; + }; + + // Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` + type TypedObjectOrdinalMembers = { + [I in Extract]: FieldType["type"]>; + }; + + // Default members + interface TypedObjectMembers { + // get/set a field by name + get(key: K): FieldType["type"]>; + set(key: K, value: FieldType["type"]>): void; + + // get/set a field by index + getIndex>(index: I): FieldType["type"]>; + setIndex>(index: I, value: FieldType["type"]>): void; + } + + // Use constrained `infer` in template literal to get ordinal indices as numbers: + type IndicesOf = NumberFor>; // ordinal indices as number literals + + type TypedObject = + & TypedObjectMembers + & TypedObjectNamedMembers + & TypedObjectOrdinalMembers; + + // NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type + type Point = TypedObject<[ + { name: "x", type: "f64" }, + { name: "y", type: "f64" }, + ]>; + + declare const p: Point; + p.getIndex(0); // ok, 0 is a valid index + p.getIndex(1); // ok, 1 is a valid index + p.getIndex(2); // error, 2 is not a valid index + ~ +!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. + + p.setIndex(0, 0); // ok, 0 is a valid index + p.setIndex(1, 0); // ok, 1 is a valid index + p.setIndex(2, 3); // error, 2 is not a valid index + ~ +!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypes4.js b/tests/baselines/reference/templateLiteralTypes4.js new file mode 100644 index 00000000000..c3654864b1d --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes4.js @@ -0,0 +1,182 @@ +//// [templateLiteralTypes4.ts] +type Is = T; + +type T0 = "100" extends `${Is}` ? N : never; // 100 +type T1 = "-100" extends `${Is}` ? N : never; // -100 +type T2 = "1.1" extends `${Is}` ? N : never; // 1.1 +type T3 = "8e-11" extends `${Is}` ? N : never; // 8e-11 (0.00000000008) +type T4 = "0x10" extends `${Is}` ? N : never; // number (not round-trippable) +type T5 = "0o10" extends `${Is}` ? N : never; // number (not round-trippable) +type T6 = "0b10" extends `${Is}` ? N : never; // number (not round-trippable) +type T7 = "10e2" extends `${Is}` ? N : never; // number (not round-trippable) +type T8 = "abcd" extends `${Is}` ? N : never; // never + +type T10 = "100" extends `${Is}` ? N : never; // 100n +type T11 = "-100" extends `${Is}` ? N : never; // -100n +type T12 = "0x10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T13 = "0o10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T14 = "0b10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T15 = "1.1" extends `${Is}` ? N : never; // never +type T16 = "10e2" extends `${Is}` ? N : never; // never +type T17 = "abcd" extends `${Is}` ? N : never; // never + +type T20 = "true" extends `${Is}` ? T : never; // true +type T21 = "false" extends `${Is}` ? T : never; // false +type T22 = "abcd" extends `${Is}` ? T : never; // never + +type T30 = "null" extends `${Is}` ? T : never; // null +type T31 = "abcd" extends `${Is}` ? T : never; // never + +type T40 = "undefined" extends `${Is}` ? T : never; // undefined +type T41 = "abcd" extends `${Is}` ? T : never; // never + +type T50 = "100" extends `${Is}` ? T : never; // "100" | 100 | 100n +type T51 = "1.1" extends `${Is}` ? T : never; // "100" | 1.1 +type T52 = "true" extends `${Is}` ? T : never; // "true" | true +type T53 = "false" extends `${Is}` ? T : never; // "false" | false +type T54 = "null" extends `${Is}` ? T : never; // "null" | null +type T55 = "undefined" extends `${Is}` ? T : never; // "undefined" | undefined + +type NumberFor = S extends `${Is}` ? N : never; +type T60 = NumberFor<"100">; // 100 +type T61 = NumberFor; // never +type T62 = NumberFor; // never + +// example use case: +interface FieldDefinition { + readonly name: string; + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; +} + +type FieldType = + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : + T extends "f32" | "f64" ? bigint : + never; + +// Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectNamedMembers = { + [P in TDef[number]["name"]]: FieldType["type"]>; +}; + +// Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectOrdinalMembers = { + [I in Extract]: FieldType["type"]>; +}; + +// Default members +interface TypedObjectMembers { + // get/set a field by name + get(key: K): FieldType["type"]>; + set(key: K, value: FieldType["type"]>): void; + + // get/set a field by index + getIndex>(index: I): FieldType["type"]>; + setIndex>(index: I, value: FieldType["type"]>): void; +} + +// Use constrained `infer` in template literal to get ordinal indices as numbers: +type IndicesOf = NumberFor>; // ordinal indices as number literals + +type TypedObject = + & TypedObjectMembers + & TypedObjectNamedMembers + & TypedObjectOrdinalMembers; + +// NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type +type Point = TypedObject<[ + { name: "x", type: "f64" }, + { name: "y", type: "f64" }, +]>; + +declare const p: Point; +p.getIndex(0); // ok, 0 is a valid index +p.getIndex(1); // ok, 1 is a valid index +p.getIndex(2); // error, 2 is not a valid index + +p.setIndex(0, 0); // ok, 0 is a valid index +p.setIndex(1, 0); // ok, 1 is a valid index +p.setIndex(2, 3); // error, 2 is not a valid index + + +//// [templateLiteralTypes4.js] +"use strict"; +p.getIndex(0); // ok, 0 is a valid index +p.getIndex(1); // ok, 1 is a valid index +p.getIndex(2); // error, 2 is not a valid index +p.setIndex(0, 0); // ok, 0 is a valid index +p.setIndex(1, 0); // ok, 1 is a valid index +p.setIndex(2, 3); // error, 2 is not a valid index + + +//// [templateLiteralTypes4.d.ts] +declare type Is = T; +declare type T0 = "100" extends `${Is}` ? N : never; +declare type T1 = "-100" extends `${Is}` ? N : never; +declare type T2 = "1.1" extends `${Is}` ? N : never; +declare type T3 = "8e-11" extends `${Is}` ? N : never; +declare type T4 = "0x10" extends `${Is}` ? N : never; +declare type T5 = "0o10" extends `${Is}` ? N : never; +declare type T6 = "0b10" extends `${Is}` ? N : never; +declare type T7 = "10e2" extends `${Is}` ? N : never; +declare type T8 = "abcd" extends `${Is}` ? N : never; +declare type T10 = "100" extends `${Is}` ? N : never; +declare type T11 = "-100" extends `${Is}` ? N : never; +declare type T12 = "0x10" extends `${Is}` ? N : never; +declare type T13 = "0o10" extends `${Is}` ? N : never; +declare type T14 = "0b10" extends `${Is}` ? N : never; +declare type T15 = "1.1" extends `${Is}` ? N : never; +declare type T16 = "10e2" extends `${Is}` ? N : never; +declare type T17 = "abcd" extends `${Is}` ? N : never; +declare type T20 = "true" extends `${Is}` ? T : never; +declare type T21 = "false" extends `${Is}` ? T : never; +declare type T22 = "abcd" extends `${Is}` ? T : never; +declare type T30 = "null" extends `${Is}` ? T : never; +declare type T31 = "abcd" extends `${Is}` ? T : never; +declare type T40 = "undefined" extends `${Is}` ? T : never; +declare type T41 = "abcd" extends `${Is}` ? T : never; +declare type T50 = "100" extends `${Is}` ? T : never; +declare type T51 = "1.1" extends `${Is}` ? T : never; +declare type T52 = "true" extends `${Is}` ? T : never; +declare type T53 = "false" extends `${Is}` ? T : never; +declare type T54 = "null" extends `${Is}` ? T : never; +declare type T55 = "undefined" extends `${Is}` ? T : never; +declare type NumberFor = S extends `${Is}` ? N : never; +declare type T60 = NumberFor<"100">; +declare type T61 = NumberFor; +declare type T62 = NumberFor; +interface FieldDefinition { + readonly name: string; + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; +} +declare type FieldType = T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : T extends "f32" | "f64" ? bigint : never; +declare type TypedObjectNamedMembers = { + [P in TDef[number]["name"]]: FieldType["type"]>; +}; +declare type TypedObjectOrdinalMembers = { + [I in Extract]: FieldType["type"]>; +}; +interface TypedObjectMembers { + get(key: K): FieldType["type"]>; + set(key: K, value: FieldType["type"]>): void; + getIndex>(index: I): FieldType["type"]>; + setIndex>(index: I, value: FieldType["type"]>): void; +} +declare type IndicesOf = NumberFor>; +declare type TypedObject = TypedObjectMembers & TypedObjectNamedMembers & TypedObjectOrdinalMembers; +declare type Point = TypedObject<[ + { + name: "x"; + type: "f64"; + }, + { + name: "y"; + type: "f64"; + } +]>; +declare const p: Point; diff --git a/tests/baselines/reference/templateLiteralTypes4.symbols b/tests/baselines/reference/templateLiteralTypes4.symbols new file mode 100644 index 00000000000..58a08396f38 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes4.symbols @@ -0,0 +1,402 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypes4.ts === +type Is = T; +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 0, 8)) +>U : Symbol(U, Decl(templateLiteralTypes4.ts, 0, 20)) +>U : Symbol(U, Decl(templateLiteralTypes4.ts, 0, 20)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 0, 8)) + +type T0 = "100" extends `${Is}` ? N : never; // 100 +>T0 : Symbol(T0, Decl(templateLiteralTypes4.ts, 0, 28)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 2, 35)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 2, 35)) + +type T1 = "-100" extends `${Is}` ? N : never; // -100 +>T1 : Symbol(T1, Decl(templateLiteralTypes4.ts, 2, 61)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 3, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 3, 36)) + +type T2 = "1.1" extends `${Is}` ? N : never; // 1.1 +>T2 : Symbol(T2, Decl(templateLiteralTypes4.ts, 3, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 4, 35)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 4, 35)) + +type T3 = "8e-11" extends `${Is}` ? N : never; // 8e-11 (0.00000000008) +>T3 : Symbol(T3, Decl(templateLiteralTypes4.ts, 4, 61)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 5, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 5, 37)) + +type T4 = "0x10" extends `${Is}` ? N : never; // number (not round-trippable) +>T4 : Symbol(T4, Decl(templateLiteralTypes4.ts, 5, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 6, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 6, 36)) + +type T5 = "0o10" extends `${Is}` ? N : never; // number (not round-trippable) +>T5 : Symbol(T5, Decl(templateLiteralTypes4.ts, 6, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 7, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 7, 36)) + +type T6 = "0b10" extends `${Is}` ? N : never; // number (not round-trippable) +>T6 : Symbol(T6, Decl(templateLiteralTypes4.ts, 7, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 8, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 8, 36)) + +type T7 = "10e2" extends `${Is}` ? N : never; // number (not round-trippable) +>T7 : Symbol(T7, Decl(templateLiteralTypes4.ts, 8, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 9, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 9, 36)) + +type T8 = "abcd" extends `${Is}` ? N : never; // never +>T8 : Symbol(T8, Decl(templateLiteralTypes4.ts, 9, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 10, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 10, 36)) + +type T10 = "100" extends `${Is}` ? N : never; // 100n +>T10 : Symbol(T10, Decl(templateLiteralTypes4.ts, 10, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 12, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 12, 36)) + +type T11 = "-100" extends `${Is}` ? N : never; // -100n +>T11 : Symbol(T11, Decl(templateLiteralTypes4.ts, 12, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 13, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 13, 37)) + +type T12 = "0x10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T12 : Symbol(T12, Decl(templateLiteralTypes4.ts, 13, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 14, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 14, 37)) + +type T13 = "0o10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T13 : Symbol(T13, Decl(templateLiteralTypes4.ts, 14, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 15, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 15, 37)) + +type T14 = "0b10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T14 : Symbol(T14, Decl(templateLiteralTypes4.ts, 15, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 16, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 16, 37)) + +type T15 = "1.1" extends `${Is}` ? N : never; // never +>T15 : Symbol(T15, Decl(templateLiteralTypes4.ts, 16, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 17, 36)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 17, 36)) + +type T16 = "10e2" extends `${Is}` ? N : never; // never +>T16 : Symbol(T16, Decl(templateLiteralTypes4.ts, 17, 62)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 18, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 18, 37)) + +type T17 = "abcd" extends `${Is}` ? N : never; // never +>T17 : Symbol(T17, Decl(templateLiteralTypes4.ts, 18, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 19, 37)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 19, 37)) + +type T20 = "true" extends `${Is}` ? T : never; // true +>T20 : Symbol(T20, Decl(templateLiteralTypes4.ts, 19, 63)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 21, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 21, 37)) + +type T21 = "false" extends `${Is}` ? T : never; // false +>T21 : Symbol(T21, Decl(templateLiteralTypes4.ts, 21, 64)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 22, 38)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 22, 38)) + +type T22 = "abcd" extends `${Is}` ? T : never; // never +>T22 : Symbol(T22, Decl(templateLiteralTypes4.ts, 22, 65)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 23, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 23, 37)) + +type T30 = "null" extends `${Is}` ? T : never; // null +>T30 : Symbol(T30, Decl(templateLiteralTypes4.ts, 23, 64)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 25, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 25, 37)) + +type T31 = "abcd" extends `${Is}` ? T : never; // never +>T31 : Symbol(T31, Decl(templateLiteralTypes4.ts, 25, 61)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 26, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 26, 37)) + +type T40 = "undefined" extends `${Is}` ? T : never; // undefined +>T40 : Symbol(T40, Decl(templateLiteralTypes4.ts, 26, 61)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 28, 42)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 28, 42)) + +type T41 = "abcd" extends `${Is}` ? T : never; // never +>T41 : Symbol(T41, Decl(templateLiteralTypes4.ts, 28, 71)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 29, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 29, 37)) + +type T50 = "100" extends `${Is}` ? T : never; // "100" | 100 | 100n +>T50 : Symbol(T50, Decl(templateLiteralTypes4.ts, 29, 66)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 31, 36)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 31, 36)) + +type T51 = "1.1" extends `${Is}` ? T : never; // "100" | 1.1 +>T51 : Symbol(T51, Decl(templateLiteralTypes4.ts, 31, 109)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 32, 36)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 32, 36)) + +type T52 = "true" extends `${Is}` ? T : never; // "true" | true +>T52 : Symbol(T52, Decl(templateLiteralTypes4.ts, 32, 109)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 33, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 33, 37)) + +type T53 = "false" extends `${Is}` ? T : never; // "false" | false +>T53 : Symbol(T53, Decl(templateLiteralTypes4.ts, 33, 110)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 34, 38)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 34, 38)) + +type T54 = "null" extends `${Is}` ? T : never; // "null" | null +>T54 : Symbol(T54, Decl(templateLiteralTypes4.ts, 34, 111)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 35, 37)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 35, 37)) + +type T55 = "undefined" extends `${Is}` ? T : never; // "undefined" | undefined +>T55 : Symbol(T55, Decl(templateLiteralTypes4.ts, 35, 110)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 36, 42)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 36, 42)) + +type NumberFor = S extends `${Is}` ? N : never; +>NumberFor : Symbol(NumberFor, Decl(templateLiteralTypes4.ts, 36, 115)) +>S : Symbol(S, Decl(templateLiteralTypes4.ts, 38, 15)) +>S : Symbol(S, Decl(templateLiteralTypes4.ts, 38, 15)) +>Is : Symbol(Is, Decl(templateLiteralTypes4.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 38, 56)) +>N : Symbol(N, Decl(templateLiteralTypes4.ts, 38, 56)) + +type T60 = NumberFor<"100">; // 100 +>T60 : Symbol(T60, Decl(templateLiteralTypes4.ts, 38, 82)) +>NumberFor : Symbol(NumberFor, Decl(templateLiteralTypes4.ts, 36, 115)) + +type T61 = NumberFor; // never +>T61 : Symbol(T61, Decl(templateLiteralTypes4.ts, 39, 28)) +>NumberFor : Symbol(NumberFor, Decl(templateLiteralTypes4.ts, 36, 115)) + +type T62 = NumberFor; // never +>T62 : Symbol(T62, Decl(templateLiteralTypes4.ts, 40, 26)) +>NumberFor : Symbol(NumberFor, Decl(templateLiteralTypes4.ts, 36, 115)) + +// example use case: +interface FieldDefinition { +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + readonly name: string; +>name : Symbol(FieldDefinition.name, Decl(templateLiteralTypes4.ts, 44, 27)) + + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; +>type : Symbol(FieldDefinition.type, Decl(templateLiteralTypes4.ts, 45, 26)) +} + +type FieldType = +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 49, 15)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 49, 15)) + + T extends "f32" | "f64" ? bigint : +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 49, 15)) + + never; + +// Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectNamedMembers = { +>TypedObjectNamedMembers : Symbol(TypedObjectNamedMembers, Decl(templateLiteralTypes4.ts, 52, 10)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 55, 29)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + [P in TDef[number]["name"]]: FieldType["type"]>; +>P : Symbol(P, Decl(templateLiteralTypes4.ts, 56, 5)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 55, 29)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 55, 29)) +>name : Symbol(name, Decl(templateLiteralTypes4.ts, 56, 66)) +>P : Symbol(P, Decl(templateLiteralTypes4.ts, 56, 5)) + +}; + +// Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectOrdinalMembers = { +>TypedObjectOrdinalMembers : Symbol(TypedObjectOrdinalMembers, Decl(templateLiteralTypes4.ts, 57, 2)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 60, 31)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + [I in Extract]: FieldType["type"]>; +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 61, 5)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 60, 31)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 60, 31)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 61, 5)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + +}; + +// Default members +interface TypedObjectMembers { +>TypedObjectMembers : Symbol(TypedObjectMembers, Decl(templateLiteralTypes4.ts, 62, 2)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + // get/set a field by name + get(key: K): FieldType["type"]>; +>get : Symbol(TypedObjectMembers.get, Decl(templateLiteralTypes4.ts, 65, 71)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 67, 8)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>key : Symbol(key, Decl(templateLiteralTypes4.ts, 67, 40)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 67, 8)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>name : Symbol(name, Decl(templateLiteralTypes4.ts, 67, 82)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 67, 8)) + + set(key: K, value: FieldType["type"]>): void; +>set : Symbol(TypedObjectMembers.set, Decl(templateLiteralTypes4.ts, 67, 112)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 68, 8)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>key : Symbol(key, Decl(templateLiteralTypes4.ts, 68, 40)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 68, 8)) +>value : Symbol(value, Decl(templateLiteralTypes4.ts, 68, 47)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>name : Symbol(name, Decl(templateLiteralTypes4.ts, 68, 88)) +>K : Symbol(K, Decl(templateLiteralTypes4.ts, 68, 8)) + + // get/set a field by index + getIndex>(index: I): FieldType["type"]>; +>getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 71, 13)) +>IndicesOf : Symbol(IndicesOf, Decl(templateLiteralTypes4.ts, 73, 1)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>index : Symbol(index, Decl(templateLiteralTypes4.ts, 71, 40)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 71, 13)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 71, 13)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + setIndex>(index: I, value: FieldType["type"]>): void; +>setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 72, 13)) +>IndicesOf : Symbol(IndicesOf, Decl(templateLiteralTypes4.ts, 73, 1)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>index : Symbol(index, Decl(templateLiteralTypes4.ts, 72, 40)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 72, 13)) +>value : Symbol(value, Decl(templateLiteralTypes4.ts, 72, 49)) +>FieldType : Symbol(FieldType, Decl(templateLiteralTypes4.ts, 47, 1)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 65, 29)) +>I : Symbol(I, Decl(templateLiteralTypes4.ts, 72, 13)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) +} + +// Use constrained `infer` in template literal to get ordinal indices as numbers: +type IndicesOf = NumberFor>; // ordinal indices as number literals +>IndicesOf : Symbol(IndicesOf, Decl(templateLiteralTypes4.ts, 73, 1)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 76, 15)) +>NumberFor : Symbol(NumberFor, Decl(templateLiteralTypes4.ts, 36, 115)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(templateLiteralTypes4.ts, 76, 15)) + +type TypedObject = +>TypedObject : Symbol(TypedObject, Decl(templateLiteralTypes4.ts, 76, 56)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 78, 17)) +>FieldDefinition : Symbol(FieldDefinition, Decl(templateLiteralTypes4.ts, 41, 28)) + + & TypedObjectMembers +>TypedObjectMembers : Symbol(TypedObjectMembers, Decl(templateLiteralTypes4.ts, 62, 2)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 78, 17)) + + & TypedObjectNamedMembers +>TypedObjectNamedMembers : Symbol(TypedObjectNamedMembers, Decl(templateLiteralTypes4.ts, 52, 10)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 78, 17)) + + & TypedObjectOrdinalMembers; +>TypedObjectOrdinalMembers : Symbol(TypedObjectOrdinalMembers, Decl(templateLiteralTypes4.ts, 57, 2)) +>TDef : Symbol(TDef, Decl(templateLiteralTypes4.ts, 78, 17)) + +// NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type +type Point = TypedObject<[ +>Point : Symbol(Point, Decl(templateLiteralTypes4.ts, 81, 38)) +>TypedObject : Symbol(TypedObject, Decl(templateLiteralTypes4.ts, 76, 56)) + + { name: "x", type: "f64" }, +>name : Symbol(name, Decl(templateLiteralTypes4.ts, 85, 5)) +>type : Symbol(type, Decl(templateLiteralTypes4.ts, 85, 16)) + + { name: "y", type: "f64" }, +>name : Symbol(name, Decl(templateLiteralTypes4.ts, 86, 5)) +>type : Symbol(type, Decl(templateLiteralTypes4.ts, 86, 16)) + +]>; + +declare const p: Point; +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>Point : Symbol(Point, Decl(templateLiteralTypes4.ts, 81, 38)) + +p.getIndex(0); // ok, 0 is a valid index +>p.getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) + +p.getIndex(1); // ok, 1 is a valid index +>p.getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) + +p.getIndex(2); // error, 2 is not a valid index +>p.getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>getIndex : Symbol(TypedObjectMembers.getIndex, Decl(templateLiteralTypes4.ts, 68, 125)) + +p.setIndex(0, 0); // ok, 0 is a valid index +>p.setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) + +p.setIndex(1, 0); // ok, 1 is a valid index +>p.setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) + +p.setIndex(2, 3); // error, 2 is not a valid index +>p.setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) +>p : Symbol(p, Decl(templateLiteralTypes4.ts, 89, 13)) +>setIndex : Symbol(TypedObjectMembers.setIndex, Decl(templateLiteralTypes4.ts, 71, 104)) + diff --git a/tests/baselines/reference/templateLiteralTypes4.types b/tests/baselines/reference/templateLiteralTypes4.types new file mode 100644 index 00000000000..e458ab1b441 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypes4.types @@ -0,0 +1,244 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypes4.ts === +type Is = T; +>Is : T + +type T0 = "100" extends `${Is}` ? N : never; // 100 +>T0 : 100 + +type T1 = "-100" extends `${Is}` ? N : never; // -100 +>T1 : -100 + +type T2 = "1.1" extends `${Is}` ? N : never; // 1.1 +>T2 : 1.1 + +type T3 = "8e-11" extends `${Is}` ? N : never; // 8e-11 (0.00000000008) +>T3 : 8e-11 + +type T4 = "0x10" extends `${Is}` ? N : never; // number (not round-trippable) +>T4 : number + +type T5 = "0o10" extends `${Is}` ? N : never; // number (not round-trippable) +>T5 : number + +type T6 = "0b10" extends `${Is}` ? N : never; // number (not round-trippable) +>T6 : number + +type T7 = "10e2" extends `${Is}` ? N : never; // number (not round-trippable) +>T7 : number + +type T8 = "abcd" extends `${Is}` ? N : never; // never +>T8 : never + +type T10 = "100" extends `${Is}` ? N : never; // 100n +>T10 : 100n + +type T11 = "-100" extends `${Is}` ? N : never; // -100n +>T11 : -100n + +type T12 = "0x10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T12 : bigint + +type T13 = "0o10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T13 : bigint + +type T14 = "0b10" extends `${Is}` ? N : never; // bigint (not round-trippable) +>T14 : bigint + +type T15 = "1.1" extends `${Is}` ? N : never; // never +>T15 : never + +type T16 = "10e2" extends `${Is}` ? N : never; // never +>T16 : never + +type T17 = "abcd" extends `${Is}` ? N : never; // never +>T17 : never + +type T20 = "true" extends `${Is}` ? T : never; // true +>T20 : true + +type T21 = "false" extends `${Is}` ? T : never; // false +>T21 : false + +type T22 = "abcd" extends `${Is}` ? T : never; // never +>T22 : never + +type T30 = "null" extends `${Is}` ? T : never; // null +>T30 : null +>null : null + +type T31 = "abcd" extends `${Is}` ? T : never; // never +>T31 : never +>null : null + +type T40 = "undefined" extends `${Is}` ? T : never; // undefined +>T40 : undefined + +type T41 = "abcd" extends `${Is}` ? T : never; // never +>T41 : never + +type T50 = "100" extends `${Is}` ? T : never; // "100" | 100 | 100n +>T50 : "100" | 100 | 100n +>null : null + +type T51 = "1.1" extends `${Is}` ? T : never; // "100" | 1.1 +>T51 : "1.1" | 1.1 +>null : null + +type T52 = "true" extends `${Is}` ? T : never; // "true" | true +>T52 : true | "true" +>null : null + +type T53 = "false" extends `${Is}` ? T : never; // "false" | false +>T53 : false | "false" +>null : null + +type T54 = "null" extends `${Is}` ? T : never; // "null" | null +>T54 : "null" | null +>null : null + +type T55 = "undefined" extends `${Is}` ? T : never; // "undefined" | undefined +>T55 : "undefined" | undefined +>null : null + +type NumberFor = S extends `${Is}` ? N : never; +>NumberFor : NumberFor + +type T60 = NumberFor<"100">; // 100 +>T60 : 100 + +type T61 = NumberFor; // never +>T61 : never + +type T62 = NumberFor; // never +>T62 : never + +// example use case: +interface FieldDefinition { + readonly name: string; +>name : string + + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; +>type : "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64" +} + +type FieldType = +>FieldType : FieldType + + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : + T extends "f32" | "f64" ? bigint : + never; + +// Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectNamedMembers = { +>TypedObjectNamedMembers : TypedObjectNamedMembers + + [P in TDef[number]["name"]]: FieldType["type"]>; +>name : P + +}; + +// Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectOrdinalMembers = { +>TypedObjectOrdinalMembers : TypedObjectOrdinalMembers + + [I in Extract]: FieldType["type"]>; +}; + +// Default members +interface TypedObjectMembers { + // get/set a field by name + get(key: K): FieldType["type"]>; +>get : (key: K) => FieldType["type"]> +>key : K +>name : K + + set(key: K, value: FieldType["type"]>): void; +>set : (key: K, value: FieldType["type"]>) => void +>key : K +>value : FieldType["type"]> +>name : K + + // get/set a field by index + getIndex>(index: I): FieldType["type"]>; +>getIndex : >>(index: I) => FieldType["type"]> +>index : I + + setIndex>(index: I, value: FieldType["type"]>): void; +>setIndex : >>(index: I, value: FieldType["type"]>) => void +>index : I +>value : FieldType["type"]> +} + +// Use constrained `infer` in template literal to get ordinal indices as numbers: +type IndicesOf = NumberFor>; // ordinal indices as number literals +>IndicesOf : IndicesOf + +type TypedObject = +>TypedObject : TypedObject + + & TypedObjectMembers + & TypedObjectNamedMembers + & TypedObjectOrdinalMembers; + +// NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type +type Point = TypedObject<[ +>Point : Point + + { name: "x", type: "f64" }, +>name : "x" +>type : "f64" + + { name: "y", type: "f64" }, +>name : "y" +>type : "f64" + +]>; + +declare const p: Point; +>p : Point + +p.getIndex(0); // ok, 0 is a valid index +>p.getIndex(0) : number +>p.getIndex : (index: I) => FieldType["type"]> +>p : Point +>getIndex : (index: I) => FieldType["type"]> +>0 : 0 + +p.getIndex(1); // ok, 1 is a valid index +>p.getIndex(1) : number +>p.getIndex : (index: I) => FieldType["type"]> +>p : Point +>getIndex : (index: I) => FieldType["type"]> +>1 : 1 + +p.getIndex(2); // error, 2 is not a valid index +>p.getIndex(2) : number +>p.getIndex : (index: I) => FieldType["type"]> +>p : Point +>getIndex : (index: I) => FieldType["type"]> +>2 : 2 + +p.setIndex(0, 0); // ok, 0 is a valid index +>p.setIndex(0, 0) : void +>p.setIndex : (index: I, value: FieldType["type"]>) => void +>p : Point +>setIndex : (index: I, value: FieldType["type"]>) => void +>0 : 0 +>0 : 0 + +p.setIndex(1, 0); // ok, 1 is a valid index +>p.setIndex(1, 0) : void +>p.setIndex : (index: I, value: FieldType["type"]>) => void +>p : Point +>setIndex : (index: I, value: FieldType["type"]>) => void +>1 : 1 +>0 : 0 + +p.setIndex(2, 3); // error, 2 is not a valid index +>p.setIndex(2, 3) : void +>p.setIndex : (index: I, value: FieldType["type"]>) => void +>p : Point +>setIndex : (index: I, value: FieldType["type"]>) => void +>2 : 2 +>3 : 3 + diff --git a/tests/cases/conformance/types/literal/templateLiteralTypes4.ts b/tests/cases/conformance/types/literal/templateLiteralTypes4.ts new file mode 100644 index 00000000000..55897258260 --- /dev/null +++ b/tests/cases/conformance/types/literal/templateLiteralTypes4.ts @@ -0,0 +1,100 @@ +// @strict: true +// @declaration: true + +type Is = T; + +type T0 = "100" extends `${Is}` ? N : never; // 100 +type T1 = "-100" extends `${Is}` ? N : never; // -100 +type T2 = "1.1" extends `${Is}` ? N : never; // 1.1 +type T3 = "8e-11" extends `${Is}` ? N : never; // 8e-11 (0.00000000008) +type T4 = "0x10" extends `${Is}` ? N : never; // number (not round-trippable) +type T5 = "0o10" extends `${Is}` ? N : never; // number (not round-trippable) +type T6 = "0b10" extends `${Is}` ? N : never; // number (not round-trippable) +type T7 = "10e2" extends `${Is}` ? N : never; // number (not round-trippable) +type T8 = "abcd" extends `${Is}` ? N : never; // never + +type T10 = "100" extends `${Is}` ? N : never; // 100n +type T11 = "-100" extends `${Is}` ? N : never; // -100n +type T12 = "0x10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T13 = "0o10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T14 = "0b10" extends `${Is}` ? N : never; // bigint (not round-trippable) +type T15 = "1.1" extends `${Is}` ? N : never; // never +type T16 = "10e2" extends `${Is}` ? N : never; // never +type T17 = "abcd" extends `${Is}` ? N : never; // never + +type T20 = "true" extends `${Is}` ? T : never; // true +type T21 = "false" extends `${Is}` ? T : never; // false +type T22 = "abcd" extends `${Is}` ? T : never; // never + +type T30 = "null" extends `${Is}` ? T : never; // null +type T31 = "abcd" extends `${Is}` ? T : never; // never + +type T40 = "undefined" extends `${Is}` ? T : never; // undefined +type T41 = "abcd" extends `${Is}` ? T : never; // never + +type T50 = "100" extends `${Is}` ? T : never; // "100" | 100 | 100n +type T51 = "1.1" extends `${Is}` ? T : never; // "100" | 1.1 +type T52 = "true" extends `${Is}` ? T : never; // "true" | true +type T53 = "false" extends `${Is}` ? T : never; // "false" | false +type T54 = "null" extends `${Is}` ? T : never; // "null" | null +type T55 = "undefined" extends `${Is}` ? T : never; // "undefined" | undefined + +type NumberFor = S extends `${Is}` ? N : never; +type T60 = NumberFor<"100">; // 100 +type T61 = NumberFor; // never +type T62 = NumberFor; // never + +// example use case: +interface FieldDefinition { + readonly name: string; + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; +} + +type FieldType = + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : + T extends "f32" | "f64" ? bigint : + never; + +// Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectNamedMembers = { + [P in TDef[number]["name"]]: FieldType["type"]>; +}; + +// Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` +type TypedObjectOrdinalMembers = { + [I in Extract]: FieldType["type"]>; +}; + +// Default members +interface TypedObjectMembers { + // get/set a field by name + get(key: K): FieldType["type"]>; + set(key: K, value: FieldType["type"]>): void; + + // get/set a field by index + getIndex>(index: I): FieldType["type"]>; + setIndex>(index: I, value: FieldType["type"]>): void; +} + +// Use constrained `infer` in template literal to get ordinal indices as numbers: +type IndicesOf = NumberFor>; // ordinal indices as number literals + +type TypedObject = + & TypedObjectMembers + & TypedObjectNamedMembers + & TypedObjectOrdinalMembers; + +// NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type +type Point = TypedObject<[ + { name: "x", type: "f64" }, + { name: "y", type: "f64" }, +]>; + +declare const p: Point; +p.getIndex(0); // ok, 0 is a valid index +p.getIndex(1); // ok, 1 is a valid index +p.getIndex(2); // error, 2 is not a valid index + +p.setIndex(0, 0); // ok, 0 is a valid index +p.setIndex(1, 0); // ok, 1 is a valid index +p.setIndex(2, 3); // error, 2 is not a valid index