1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
|
//// [templateLiteralTypes1.ts]
// Template types example from #12754
const createScopedActionType = <S extends string>(scope: S) => <T extends string>(type: T) => `${scope}/${type}` as `${S}/${T}`;
const createActionInMyScope = createScopedActionType("MyScope"); // <T extends string>(type: T) => `MyScope/${T}`
const MY_ACTION = createActionInMyScope("MY_ACTION"); // 'MyScope/MY_ACTION'
// Union types are distributed over template types
type EventName<S extends string> = `${S}Changed`;
type EN1 = EventName<'Foo' | 'Bar' | 'Baz'>;
type Loc = `${'top' | 'middle' | 'bottom'}-${'left' | 'center' | 'right'}`;
// Primitive literal types can be spread into templates
type ToString<T extends string | number | boolean | bigint> = `${T}`;
type TS1 = ToString<'abc' | 42 | true | -1234n>;
// Nested template literal type normalization
type TL1<T extends string> = `a${T}b${T}c`;
type TL2<U extends string> = TL1<`x${U}y`>; // `ax${U}ybx{$U}yc`
type TL3 = TL2<'o'>; // 'axoybxoyc'
// Casing intrinsics
type Cases<T extends string> = `${Uppercase<T>} ${Lowercase<T>} ${Capitalize<T>} ${Uncapitalize<T>}`;
type TCA1 = Cases<'bar'>; // 'BAR bar Bar bar'
type TCA2 = Cases<'BAR'>; // 'BAR bar BAR bAR'
// Assignability
function test<T extends 'foo' | 'bar'>(name: `get${Capitalize<T>}`) {
let s1: string = name;
let s2: 'getFoo' | 'getBar' = name;
}
function fa1<T>(x: T, y: { [P in keyof T]: T[P] }, z: { [P in keyof T & string as `p_${P}`]: T[P] }) {
y = x;
z = x; // Error
}
function fa2<T, U extends T, A extends string, B extends A>(x: { [P in B as `p_${P}`]: T }, y: { [Q in A as `p_${Q}`]: U }) {
x = y;
y = x; // Error
}
// String transformations using recursive conditional types
type Join<T extends unknown[], D extends string> =
T extends [] ? '' :
T extends [string | number | boolean | bigint] ? `${T[0]}` :
T extends [string | number | boolean | bigint, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
string;
type TJ1 = Join<[1, 2, 3, 4], '.'>
type TJ2 = Join<['foo', 'bar', 'baz'], '-'>;
type TJ3 = Join<[], '.'>
// Inference based on delimiters
type MatchPair<S extends string> = S extends `[${infer A},${infer B}]` ? [A, B] : unknown;
type T20 = MatchPair<'[1,2]'>; // ['1', '2']
type T21 = MatchPair<'[foo,bar]'>; // ['foo', 'bar']
type T22 = MatchPair<' [1,2]'>; // unknown
type T23 = MatchPair<'[123]'>; // unknown
type T24 = MatchPair<'[1,2,3,4]'>; // ['1', '2,3,4']
type SnakeToCamelCase<S extends string> =
S extends `${infer T}_${infer U}` ? `${Lowercase<T>}${SnakeToPascalCase<U>}` :
S extends `${infer T}` ? `${Lowercase<T>}` :
SnakeToPascalCase<S>;
type SnakeToPascalCase<S extends string> =
string extends S ? string :
S extends `${infer T}_${infer U}` ? `${Capitalize<Lowercase<T>>}${SnakeToPascalCase<U>}` :
S extends `${infer T}` ? `${Capitalize<Lowercase<T>>}` :
never;
type RR0 = SnakeToPascalCase<'hello_world_foo'>; // 'HelloWorldFoo'
type RR1 = SnakeToPascalCase<'FOO_BAR_BAZ'>; // 'FooBarBaz'
type RR2 = SnakeToCamelCase<'hello_world_foo'>; // 'helloWorldFoo'
type RR3 = SnakeToCamelCase<'FOO_BAR_BAZ'>; // 'fooBarBaz'
// Single character inference
type FirstTwoAndRest<S extends string> = S extends `${infer A}${infer B}${infer R}` ? [`${A}${B}`, R] : unknown;
type T25 = FirstTwoAndRest<'abcde'>; // ['ab', 'cde']
type T26 = FirstTwoAndRest<'ab'>; // ['ab', '']
type T27 = FirstTwoAndRest<'a'>; // unknown
type HexDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |'8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
type HexColor<S extends string> =
S extends `#${infer R1}${infer R2}${infer G1}${infer G2}${infer B1}${infer B2}` ?
[R1, R2, G1, G2, B1, B2] extends [HexDigit, HexDigit, HexDigit, HexDigit, HexDigit, HexDigit] ?
S :
never :
never;
type TH1 = HexColor<'#8080FF'>; // '#8080FF'
type TH2 = HexColor<'#80c0ff'>; // '#80c0ff'
type TH3 = HexColor<'#8080F'>; // never
type TH4 = HexColor<'#8080FFF'>; // never
// Recursive inference
type Trim<S extends string> =
S extends ` ${infer T}` ? Trim<T> :
S extends `${infer T} ` ? Trim<T> :
S;
type TR1 = Trim<'xx '>; // 'xx'
type TR2 = Trim<' xx'>; // 'xx'
type TR3 = Trim<' xx '>; // 'xx'
type Split<S extends string, D extends string> =
string extends S ? string[] :
S extends '' ? [] :
S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
[S];
type T40 = Split<'foo', '.'>; // ['foo']
type T41 = Split<'foo.bar.baz', '.'>; // ['foo', 'bar', 'baz']
type T42 = Split<'foo.bar', ''>; // ['f', 'o', 'o', '.', 'b', 'a', 'r']
type T43 = Split<any, '.'>; // string[]
// Inference and property name paths
declare function getProp<T, P0 extends keyof T & string, P1 extends keyof T[P0] & string, P2 extends keyof T[P0][P1] & string>(obj: T, path: `${P0}.${P1}.${P2}`): T[P0][P1][P2];
declare function getProp<T, P0 extends keyof T & string, P1 extends keyof T[P0] & string>(obj: T, path: `${P0}.${P1}`): T[P0][P1];
declare function getProp<T, P0 extends keyof T & string>(obj: T, path: P0): T[P0];
declare function getProp(obj: object, path: string): unknown;
let p1 = getProp({ a: { b: {c: 42, d: 'hello' }}} as const, 'a');
let p2 = getProp({ a: { b: {c: 42, d: 'hello' }}} as const, 'a.b');
let p3 = getProp({ a: { b: {c: 42, d: 'hello' }}} as const, 'a.b.d');
type PropType<T, Path extends string> =
string extends Path ? unknown :
Path extends keyof T ? T[Path] :
Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
unknown;
declare function getPropValue<T, P extends string>(obj: T, path: P): PropType<T, P>;
declare const s: string;
const obj = { a: { b: {c: 42, d: 'hello' }}};
getPropValue(obj, 'a'); // { b: {c: number, d: string } }
getPropValue(obj, 'a.b'); // {c: number, d: string }
getPropValue(obj, 'a.b.d'); // string
getPropValue(obj, 'a.b.x'); // unknown
getPropValue(obj, s); // unknown
// Infer type variables in template literals have string constraint
type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
type S2<S extends string> = S;
// Check that infer T declarations are validated
type TV1 = `${infer X}`;
// Batched single character inferences for lower recursion depth
type Chars<S extends string> =
string extends S ? string[] :
S extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer R}` ? [C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, ...Chars<R>] :
S extends `${infer C}${infer R}` ? [C, ...Chars<R>] :
S extends '' ? [] :
never;
type L1 = Chars<'FooBarBazThisIsALongerString'>; // ['F', 'o', 'o', 'B', 'a', 'r', ...]
// Infer never when source isn't a literal type that matches the pattern
type Foo<T> = T extends `*${infer S}*` ? S : never;
type TF1 = Foo<any>; // never
type TF2 = Foo<string>; // never
type TF3 = Foo<'abc'>; // never
type TF4 = Foo<'*abc*'>; // 'abc'
// Cross product unions limited to 100,000 constituents
type A = any;
type U1 = {a1:A} | {b1:A} | {c1:A} | {d1:A} | {e1:A} | {f1:A} | {g1:A} | {h1:A} | {i1:A} | {j1:A};
type U2 = {a2:A} | {b2:A} | {c2:A} | {d2:A} | {e2:A} | {f2:A} | {g2:A} | {h2:A} | {i2:A} | {j2:A};
type U3 = {a3:A} | {b3:A} | {c3:A} | {d3:A} | {e3:A} | {f3:A} | {g3:A} | {h3:A} | {i3:A} | {j3:A};
type U4 = {a4:A} | {b4:A} | {c4:A} | {d4:A} | {e4:A} | {f4:A} | {g4:A} | {h4:A} | {i4:A} | {j4:A};
type U5 = {a5:A} | {b5:A} | {c5:A} | {d5:A} | {e5:A} | {f5:A} | {g5:A} | {h5:A} | {i5:A} | {j5:A};
type U100000 = U1 & U2 & U3 & U4 & U5; // Error
type Digits = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type D100000 = `${Digits}${Digits}${Digits}${Digits}${Digits}`; // Error
type TDigits = [0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9];
type T100000 = [...TDigits, ...TDigits, ...TDigits, ...TDigits, ...TDigits]; // Error
// Repro from #40863
type IsNegative<T extends number> = `${T}` extends `-${string}` ? true : false;
type AA<T extends number, Q extends number> =
[true, true] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
type BB = AA<-2, -2>;
// Repro from #40970
type PathKeys<T> =
unknown extends T ? never :
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
const obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
// Repro from #46480
export type Spacing =
| `0`
| `${number}px`
| `${number}rem`
| `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
const spacing: Spacing = "s12"
export type SpacingShorthand =
| `${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
const test1: SpacingShorthand = "0 0 0";
//// [templateLiteralTypes1.js]
"use strict";
// Template types example from #12754
Object.defineProperty(exports, "__esModule", { value: true });
var createScopedActionType = function (scope) { return function (type) { return "".concat(scope, "/").concat(type); }; };
var createActionInMyScope = createScopedActionType("MyScope"); // <T extends string>(type: T) => `MyScope/${T}`
var MY_ACTION = createActionInMyScope("MY_ACTION"); // 'MyScope/MY_ACTION'
// Assignability
function test(name) {
var s1 = name;
var s2 = name;
}
function fa1(x, y, z) {
y = x;
z = x; // Error
}
function fa2(x, y) {
x = y;
y = x; // Error
}
var p1 = getProp({ a: { b: { c: 42, d: 'hello' } } }, 'a');
var p2 = getProp({ a: { b: { c: 42, d: 'hello' } } }, 'a.b');
var p3 = getProp({ a: { b: { c: 42, d: 'hello' } } }, 'a.b.d');
var obj = { a: { b: { c: 42, d: 'hello' } } };
getPropValue(obj, 'a'); // { b: {c: number, d: string } }
getPropValue(obj, 'a.b'); // {c: number, d: string }
getPropValue(obj, 'a.b.d'); // string
getPropValue(obj, 'a.b.x'); // unknown
getPropValue(obj, s); // unknown
var obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
};
var make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
var spacing = "s12";
var test1 = "0 0 0";
//// [templateLiteralTypes1.d.ts]
export type Spacing = `0` | `${number}px` | `${number}rem` | `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
export type SpacingShorthand = `${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
|