|
| 1 | +tests/cases/conformance/types/literal/templateLiteralTypes4.ts(93,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. |
| 2 | +tests/cases/conformance/types/literal/templateLiteralTypes4.ts(97,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. |
| 3 | + |
| 4 | + |
| 5 | +==== tests/cases/conformance/types/literal/templateLiteralTypes4.ts (2 errors) ==== |
| 6 | + type Is<T extends U, U> = T; |
| 7 | + |
| 8 | + type T0 = "100" extends `${Is<infer N, number>}` ? N : never; // 100 |
| 9 | + type T1 = "-100" extends `${Is<infer N, number>}` ? N : never; // -100 |
| 10 | + type T2 = "1.1" extends `${Is<infer N, number>}` ? N : never; // 1.1 |
| 11 | + type T3 = "8e-11" extends `${Is<infer N, number>}` ? N : never; // 8e-11 (0.00000000008) |
| 12 | + type T4 = "0x10" extends `${Is<infer N, number>}` ? N : never; // number (not round-trippable) |
| 13 | + type T5 = "0o10" extends `${Is<infer N, number>}` ? N : never; // number (not round-trippable) |
| 14 | + type T6 = "0b10" extends `${Is<infer N, number>}` ? N : never; // number (not round-trippable) |
| 15 | + type T7 = "10e2" extends `${Is<infer N, number>}` ? N : never; // number (not round-trippable) |
| 16 | + type T8 = "abcd" extends `${Is<infer N, number>}` ? N : never; // never |
| 17 | + |
| 18 | + type T10 = "100" extends `${Is<infer N, bigint>}` ? N : never; // 100n |
| 19 | + type T11 = "-100" extends `${Is<infer N, bigint>}` ? N : never; // -100n |
| 20 | + type T12 = "0x10" extends `${Is<infer N, bigint>}` ? N : never; // bigint (not round-trippable) |
| 21 | + type T13 = "0o10" extends `${Is<infer N, bigint>}` ? N : never; // bigint (not round-trippable) |
| 22 | + type T14 = "0b10" extends `${Is<infer N, bigint>}` ? N : never; // bigint (not round-trippable) |
| 23 | + type T15 = "1.1" extends `${Is<infer N, bigint>}` ? N : never; // never |
| 24 | + type T16 = "10e2" extends `${Is<infer N, bigint>}` ? N : never; // never |
| 25 | + type T17 = "abcd" extends `${Is<infer N, bigint>}` ? N : never; // never |
| 26 | + |
| 27 | + type T20 = "true" extends `${Is<infer T, boolean>}` ? T : never; // true |
| 28 | + type T21 = "false" extends `${Is<infer T, boolean>}` ? T : never; // false |
| 29 | + type T22 = "abcd" extends `${Is<infer T, boolean>}` ? T : never; // never |
| 30 | + |
| 31 | + type T30 = "null" extends `${Is<infer T, null>}` ? T : never; // null |
| 32 | + type T31 = "abcd" extends `${Is<infer T, null>}` ? T : never; // never |
| 33 | + |
| 34 | + type T40 = "undefined" extends `${Is<infer T, undefined>}` ? T : never; // undefined |
| 35 | + type T41 = "abcd" extends `${Is<infer T, undefined>}` ? T : never; // never |
| 36 | + |
| 37 | + type T50 = "100" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "100" | 100 | 100n |
| 38 | + type T51 = "1.1" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "100" | 1.1 |
| 39 | + type T52 = "true" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "true" | true |
| 40 | + type T53 = "false" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "false" | false |
| 41 | + type T54 = "null" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "null" | null |
| 42 | + type T55 = "undefined" extends `${Is<infer T, string | number | bigint | boolean | null | undefined>}` ? T : never; // "undefined" | undefined |
| 43 | + |
| 44 | + type NumberFor<S extends string> = S extends `${Is<infer N, number>}` ? N : never; |
| 45 | + type T60 = NumberFor<"100">; // 100 |
| 46 | + type T61 = NumberFor<any>; // never |
| 47 | + type T62 = NumberFor<never>; // never |
| 48 | + |
| 49 | + // example use case: |
| 50 | + interface FieldDefinition { |
| 51 | + readonly name: string; |
| 52 | + readonly type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" | "f32" | "f64"; |
| 53 | + } |
| 54 | + |
| 55 | + type FieldType<T extends FieldDefinition["type"]> = |
| 56 | + T extends "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" ? number : |
| 57 | + T extends "f32" | "f64" ? bigint : |
| 58 | + never; |
| 59 | + |
| 60 | + // Generates named members like `{ x: number, y: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` |
| 61 | + type TypedObjectNamedMembers<TDef extends readonly FieldDefinition[]> = { |
| 62 | + [P in TDef[number]["name"]]: FieldType<Extract<TDef[number], { readonly name: P }>["type"]>; |
| 63 | + }; |
| 64 | + |
| 65 | + // Generates ordinal members like `{ 0: number, 1: bigint }` from `[{ name: "x", type: "i32" }, { name: "y", type: "i64" }]` |
| 66 | + type TypedObjectOrdinalMembers<TDef extends readonly FieldDefinition[]> = { |
| 67 | + [I in Extract<keyof TDef, `${number}`>]: FieldType<Extract<TDef[I], FieldDefinition>["type"]>; |
| 68 | + }; |
| 69 | + |
| 70 | + // Default members |
| 71 | + interface TypedObjectMembers<TDef extends readonly FieldDefinition[]> { |
| 72 | + // get/set a field by name |
| 73 | + get<K extends TDef[number]["name"]>(key: K): FieldType<Extract<TDef[number], { readonly name: K }>["type"]>; |
| 74 | + set<K extends TDef[number]["name"]>(key: K, value: FieldType<Extract<TDef[number], { readonly name: K }>["type"]>): void; |
| 75 | + |
| 76 | + // get/set a field by index |
| 77 | + getIndex<I extends IndicesOf<TDef>>(index: I): FieldType<Extract<TDef[I], FieldDefinition>["type"]>; |
| 78 | + setIndex<I extends IndicesOf<TDef>>(index: I, value: FieldType<Extract<TDef[I], FieldDefinition>["type"]>): void; |
| 79 | + } |
| 80 | + |
| 81 | + // Use constrained `infer` in template literal to get ordinal indices as numbers: |
| 82 | + type IndicesOf<T> = NumberFor<Extract<keyof T, string>>; // ordinal indices as number literals |
| 83 | + |
| 84 | + type TypedObject<TDef extends readonly FieldDefinition[]> = |
| 85 | + & TypedObjectMembers<TDef> |
| 86 | + & TypedObjectNamedMembers<TDef> |
| 87 | + & TypedObjectOrdinalMembers<TDef>; |
| 88 | + |
| 89 | + // NOTE: type would normally be created from something like `const Point = TypedObject([...])` from which we would infer the type |
| 90 | + type Point = TypedObject<[ |
| 91 | + { name: "x", type: "f64" }, |
| 92 | + { name: "y", type: "f64" }, |
| 93 | + ]>; |
| 94 | + |
| 95 | + declare const p: Point; |
| 96 | + p.getIndex(0); // ok, 0 is a valid index |
| 97 | + p.getIndex(1); // ok, 1 is a valid index |
| 98 | + p.getIndex(2); // error, 2 is not a valid index |
| 99 | + ~ |
| 100 | +!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. |
| 101 | + |
| 102 | + p.setIndex(0, 0); // ok, 0 is a valid index |
| 103 | + p.setIndex(1, 0); // ok, 1 is a valid index |
| 104 | + p.setIndex(2, 3); // error, 2 is not a valid index |
| 105 | + ~ |
| 106 | +!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'. |
| 107 | + |
0 commit comments