1 element advanced
2 tag
3 ast
4 category
5 item
6 title Intersection Types
7 expected
8 + function extend<T, U>(first: T, second: U): T & U {
9 + let result = {} as T & U;
10 + for (let id in first) {
11 + (result as any)[id] = (first as any)[id];
12 + }
13 + for (let id in second) {
14 + if (!result.hasOwnProperty(id)) {
15 + (result as any)[id] = (second as any)[id];
16 + }
17 + }
18 + return result;
19 + }
20 + class Person {
21 + constructor(public name: string) {}
22 + }
23 + interface Loggable {
24 + log(): void;
25 + }
26 + class ConsoleLogger implements Loggable {
27 + log() {
28 + // ...
29 + }
30 + }
31 + var jim = extend(new Person("Jim"), new ConsoleLogger());
32 + var n = jim.name;
33 + jim.log();
34 ittf
35 +
36 function extend
37 :< T
38 :< U
39 param first
40 :ref T
41 param second
42 :ref U
43 :return
44 :intersect
45 :ref T
46 :ref U
47 let result
48 :as
49 :intersect
50 :ref T
51 :ref U
52 { {}
53 for let id in first
54 set =
55 @expr
56 ()
57 @id result
58 :as
59 :any
60 .[ id
61 @expr
62 ()
63 @id first
64 :as
65 :any
66 .[ id
67 for let id in second
68 if !result.hasOwnProperty(id)
69 set =
70 @expr
71 ()
72 @id result
73 :as
74 :any
75 .[ id
76 @expr
77 ()
78 @id second
79 :as
80 :any
81 .[ id
82 return result
83 class Person
84 ctor
85 param name
86 :string
87 :interface Loggable
88 :m log
89 :void
90 class ConsoleLogger
91 :extends Loggable
92 m log
93 var jim = extend(new Person("Jim"), new ConsoleLogger())
94 var n = jim.name
95 _ jim.log
96 item
97 title Union Types - fail at run time
98 expected
99 + function padLeft(value: string, padding: any) {
100 + if (typeof padding === "number") {
101 + return Array(padding + 1).join(" ") + value;
102 + }
103 + if (typeof padding === "string") {
104 + return padding + value;
105 + }
106 + throw new Error(`Expected string or number, got '${padding}'.`);
107 + }
108 + padLeft("Hello world", 4); // returns " Hello world"
109 + let indentedString = padLeft("Hello world", true); // passes at compile time, fails at runtime.
110 ittf
111 +
112 function padLeft
113 param value
114 :string
115 param padding
116 :any
117 if typeof padding === "number"
118 return Array(padding + 1).join(" ") + value
119 if typeof padding === "string"
120 return padding + value
121 throw
122 new Error
123 template
124 + Expected string or number, got '
125 @ padding
126 + '.
127 _ padLeft("Hello world", 4)
128 let indentedString = padLeft("Hello world", true)
129 item
130 title Union Types - succeds at run time
131 expected
132 + function padLeft(value: string, padding: string | number) {
133 + // ...
134 + }
135 + let indentedString = padLeft("Hello world", true); // errors during compilation
136 ittf
137 +
138 function padLeft
139 param value
140 :string
141 param padding
142 :union
143 :string
144 :number
145 let indentedString = padLeft("Hello world", true)
146 item
147 title Union Types - common members
148 expected
149 + // If we have a value that has a union type, we can only access members that are common to all types in the union.
150 + interface Bird {
151 + fly();
152 + layEggs();
153 + }
154 + interface Fish {
155 + swim();
156 + layEggs();
157 + }
158 + function getSmallPet(): Fish | Bird {
159 + // ...
160 + }
161 ittf
162 +
163 :interface Bird
164 :m fly
165 :m layEggs
166 :interface Fish
167 :m swim
168 :m layEggs
169 function getSmallPet
170 :return
171 :union
172 :ref Fish
173 :ref Bird
174 let pet = getSmallPet()
175 _ pet.layEggs
176 _ pet.swim
177 item
178 title Type Guards and Differentiating Types
179 expected
180 + let pet = getSmallPet();
181 + if ((pet as Fish).swim) {
182 + (pet as Fish).swim();
183 + } else {
184 + (pet as Bird).fly();
185 + }
186 ittf
187 +
188 let pet = getSmallPet()
189 if
190 test
191 @expr
192 ()
193 @id pet
194 :as
195 :ref Fish
196 . swim
197 _
198 ()
199 @id pet
200 :as
201 :ref Fish
202 ._ swim
203 else
204 _
205 ()
206 @id pet
207 :as
208 :ref Bird
209 ._ fly
210 item
211 title User - Defined Type Guards
212 expected
213 + function isFish(pet: Fish | Bird): pet is Fish {
214 + return (pet as Fish).swim !== undefined;
215 + }
216 + function isNumber(x: any): x is number {
217 + return typeof x === "number";
218 + }
219 + function isString(x: any): x is string {
220 + return typeof x === "string";
221 + }
222 + function padLeft(value: string, padding: string | number) {
223 + if (isNumber(padding)) {
224 + return Array(padding + 1).join(" ") + value;
225 + }
226 + if (isString(padding)) {
227 + return padding + value;
228 + }
229 + throw new Error(`Expected string or number, got '${padding}'.`);
230 + }
231 ittf
232 +
233 function isFish
234 param pet
235 :union
236 :ref Fish
237 :ref Bird
238 :return
239 :predicate pet
240 :ref Fish
241 return
242 !==
243 @expr
244 ()
245 @id pet
246 :as
247 :ref Fish
248 . swim
249 + undefined
250 function isNumber
251 param x
252 :any
253 :return
254 :predicate x
255 :number
256 return typeof x === "number"
257 function isString
258 param x
259 :any
260 :return
261 :predicate x
262 :string
263 return typeof x === "string"
264 function padLeft
265 param value
266 :string
267 param padding
268 :union
269 :string
270 :number
271 if isNumber(padding)
272 return Array(padding + 1).join(" ") + value
273 if isString(padding)
274 return padding + value
275 throw
276 new Error
277 template
278 + Expected string or number, got '
279 @ padding
280 + '.
281 item
282 title Instanceof type guards
283 expected
284 + interface Padder {
285 + getPaddingString(): string
286 + }
287 + class SpaceRepeatingPadder implements Padder {
288 + constructor(private numSpaces: number) {}
289 + getPaddingString() {
290 + return Array(this.numSpaces + 1).join(" ");
291 + }
292 + }
293 + class StringPadder implements Padder {
294 + constructor(private value: string) { }
295 + getPaddingString() {
296 + return this.value;
297 + }
298 + }
299 + function getRandomPadder() {
300 + return Math.random() < 0.5 ?
301 + new SpaceRepeatingPadder(4) :
302 + new StringPadder(" ");
303 + }
304 + // Type is 'SpaceRepeatingPadder | StringPadder'
305 + let padder: Padder = getRandomPadder();
306 + if (padder instanceof SpaceRepeatingPadder) {
307 + var x = padder; // type narrowed to 'SpaceRepeatingPadder'
308 + }
309 + if (padder instanceof StringPadder) {
310 + var x = padder; // type narrowed to 'StringPadder'
311 + }
312 ittf
313 +
314 :interface Padder
315 :m getPaddingString
316 :string
317 class SpaceRepeatingPadder
318 :extends Padder
319 ctor
320 param numSpaces
321 :private
322 :number
323 m getPaddingString
324 return Array(this.numSpaces + 1).join(" ")
325 class StringPadder
326 :extends Padder
327 ctor
328 param value
329 :private
330 :string
331 m getPaddingString
332 return this.value
333 function getRandomPadder
334 return
335 iif Math.random() < 0.5
336 then new SpaceRepeatingPadder(4)
337 else new StringPadder(" ")
338 # Type is 'SpaceRepeatingPadder | StringPadder'
339 let padder
340 :ref Padder
341 _ getRandomPadder
342 if padder instanceof SpaceRepeatingPadder
343 var x = padder
344 # type narrowed to 'SpaceRepeatingPadder'
345 if padder instanceof StringPadder
346 var x = padder
347 # type narrowed to 'StringPadder'
348 item
349 title Nullable types
350 expected
351 + let s = "foo";
352 + s = null; // error, 'null' is not assignable to 'string'
353 + let sn: string | null = "bar";
354 + sn = null; // ok
355 + sn = undefined; // error, 'undefined' is not assignable to 'string | null'
356 + // Note that TypeScript treats null and undefined differently in order to match JavaScript semantics.
357 + // string | null is a different type than string | undefined and string | undefined | null.
358 + /*
359 + Optional parameters and properties
360 + With --strictNullChecks, an optional parameter automatically adds | undefined:
361 + */
362 + function f(x: number, y?: number) {
363 + return x + (y || 0);
364 + }
365 + f(1, 2);
366 + f(1);
367 + f(1, undefined);
368 + f(1, null); // error, 'null' is not assignable to 'number | undefined'
369 + // The same is true for optional properties:
370 + class C {
371 + a: number;
372 + b?: number;
373 + }
374 + let c = new C();
375 + c.a = 12;
376 + c.a = undefined; // error, 'undefined' is not assignable to 'number'
377 + c.b = 13;
378 + c.b = undefined; // ok
379 + c.b = null; // error, 'null' is not assignable to 'number | undefined'
380 ittf
381 +
382 let s = "foo"
383 set s = null
384 let sn
385 :union
386 :string
387 :null
388 := "bar"
389 set sn = null
390 set sn = undefined
391 function f
392 param x
393 :number
394 param
395 :number
396 :optional
397 return x + y || 0
398 _ f(1, 2)
399 _ f(1)
400 _ f(1, undefined)
401 _ f(1, null)
402 class C
403 p a
404 :number
405 p b
406 :number
407 let c = new C()
408 set c.a = 12
409 set c.a = undefined
410 set c.b = 13
411 set c.b = undefined
412 set c.b = null
413 item
414 title Type guards and type assertions
415 expected
416 + function f(sn: string | null): string {
417 + if (sn == null) {
418 + return "default";
419 + } else {
420 + return sn;
421 + }
422 + }
423 + // The null elimination is pretty obvious here, but you can use terser operators too:
424 + function f(sn: string | null): string {
425 + return sn || "default";
426 + }
427 + // In cases where the compiler can’t eliminate null or undefined,
428 + // you can use the type assertion operator to manually remove them.
429 + // The syntax is postfix !: identifier! removes null and undefined from the type of identifier:
430 + function broken(name: string | null): string {
431 + function postfix(epithet: string) {
432 + return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
433 + }
434 + name = name || "Bob";
435 + return postfix("great");
436 + }
437 + function fixed(name: string | null): string {
438 + function postfix(epithet: string) {
439 + return name!.charAt(0) + '. the ' + epithet; // ok
440 + }
441 + name = name || "Bob";
442 + return postfix("great");
443 + }
444 ittf
445 +
446 function f
447 param sn
448 :union
449 :string
450 :null
451 :return
452 :string
453 if sn == null
454 return "default"
455 else
456 return sn
457 function f
458 param sn
459 :union
460 :string
461 :null
462 :return
463 :string
464 return sn || "default"
465 function broken
466 param name
467 :union
468 :string
469 :null
470 :return
471 :string
472 function postfix
473 param epithet
474 :string
475 return name.charAt(0) + '. the ' + epithet
476 set name = name || "Bob"
477 return postfix("great")
478 function fixed
479 param name
480 :union
481 :string
482 :null
483 :return
484 :string
485 function postfix
486 param epithet
487 :string
488 return
489 op+
490 op+
491 _
492 ._ charAt
493 @ 0
494 + '. the '
495 + epithet
496 set name = name || "Bob"
497 return postfix("great")
498 item
499 title Type Aliases
500 expected
501 + type Name = string;
502 + type NameResolver = () => string;
503 + type NameOrResolver = Name | NameResolver;
504 + function getName(n: NameOrResolver): Name {
505 + if (typeof n === "string") {
506 + return n;
507 + } else {
508 + return n();
509 + }
510 + }
511 + // Aliasing doesn’t actually create a new type - it creates a new name to refer to that type.
512 + // Aliasing a primitive is not terribly useful, though it can be used as a form of documentation.
513 + // Just like interfaces, type aliases can also be generic - we can just add type parameters and use them on the right side of the alias declaration:
514 + type Container<T> = { value: T };
515 + // We can also have a type alias refer to itself in a property:
516 + type Tree<T> = {
517 + value: T;
518 + left: Tree<T>;
519 + right: Tree<T>;
520 + }
521 + // Together with intersection types, we can make some pretty mind-bending types:
522 + type LinkedList<T> = T & { next: LinkedList<T> };
523 + interface Person {
524 + name: string;
525 + }
526 + var people: LinkedList<Person>;
527 + var s = people.name;
528 + var s = people.next.name;
529 + var s = people.next.next.name;
530 + var s = people.next.next.next.name;
531 + // However, it’s not possible for a type alias to appear anywhere else on the right side of the declaration:
532 + type Yikes = Array<Yikes>; // error
533 ittf
534 +
535 :type Name
536 :string
537 :type NameResolver
538 :=>
539 :string
540 :type NameOrResolver
541 :union
542 :ref Name
543 :ref NameResolver
544 function getName
545 param n
546 :ref NameOrResolver
547 :return
548 :ref Name
549 if typeof n === "string"
550 return n
551 else
552 return n()
553 :type Container
554 :< T
555 :{
556 :p value
557 :ref T
558 :type Tree
559 :< T
560 :{
561 :p value
562 :ref T
563 :p left
564 :ref Tree
565 :ref T
566 :p right
567 :ref Tree
568 :ref T
569 :type LinkedList
570 :< T
571 :intersect
572 :ref T
573 :{
574 :p next
575 :ref LinkedList
576 :ref T
577 :interface Person
578 :p name
579 :string
580 var people
581 :ref LinkedList
582 :ref Person
583 var s = people.name
584 var s = people.next.name
585 var s = people.next.next.name
586 var s = people.next.next.next.name
587 :type Yikes
588 :ref Array
589 :ref Yikes
590 item
591 title Interfaces vs.Type Aliases
592 expected
593 +
594 ittf
595 +
596 :type Alias
597 :{
598 :p num
599 :number
600 :interface Interface
601 :p num
602 :number
603 :function aliased
604 param arg
605 :ref Alias
606 :return
607 :ref Alias
608 :function interfaced
609 param arg
610 :ref Interface
611 :return
612 :ref Interface
613 item
614 title String Literal Types
615 expected
616 + type Easing = "ease-in" | "ease-out" | "ease-in-out";
617 + class UIElement {
618 + animate(dx: number, dy: number, easing: Easing) {
619 + if (easing === "ease-in") {
620 + } else if (easing === "ease-out") {
621 + } else if (easing === "ease-in-out") {
622 + } else {
623 + // error! should not pass null or undefined.
624 + }
625 + }
626 + }
627 + let button = new UIElement();
628 + button.animate(0, 0, "ease-in");
629 + button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
630 + // You can pass any of the three allowed strings, but any other string will give the error
631 + // Argument of type '"uneasy"' is not assignable to parameter of type '"ease-in" | "ease-out" | "ease-in-out"'
632 + // String literal types can be used in the same way to distinguish overloads:
633 + function createElement(tagName: "img"): HTMLImageElement;
634 + function createElement(tagName: "input"): HTMLInputElement;
635 + // ... more overloads ...
636 + function createElement(tagName: string): Element {
637 + // ... code goes here ...
638 + }
639 ittf
640 +
641 :type Easing
642 :union
643 :literal "ease-in"
644 :literal "ease-out"
645 :literal "ease-in-out"
646 class UIElement
647 m animate
648 param dx
649 :number
650 param dy
651 :number
652 param easing
653 :ref Easing
654 if easing === "ease-in"
655 else
656 if easing === "ease-out"
657 else
658 if easing === "ease-in-out"
659 else
660 let button = new UIElement()
661 _ button.animate(0, 0, "ease-in")
662 _ button.animate(0, 0, "uneasy")
663 :function createElement
664 param tagName
665 :literal "img"
666 :return
667 :ref HTMLImageElement
668 :function createElement
669 param tagName
670 :literal "input"
671 :return
672 :ref HTMLInputElement
673 function createElement
674 param tagName
675 :string
676 :return
677 :ref Element
678 item
679 title Numeric Literal Types
680 expected
681 + function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
682 + }
683 + // These are seldom written explicitly, they can be useful when narrowing can catch bugs:
684 + function foo(x: number) {
685 + if (x !== 1 || x !== 2) {
686 + // Operator '!==' cannot be applied to types '1' and '2'.
687 + }
688 + }
689 + // In other words, x must be 1 when it gets compared to 2, meaning that the above check is making an invalid comparison.
690 ittf
691 +
692 function rollDie
693 :return
694 :union
695 :literal 1
696 :literal 2
697 :literal 3
698 :literal 4
699 :literal 5
700 :literal 6
701 function foo
702 param x
703 :number
704 if x !== 1 || x !== 2
705 item
706 title Enum Member Types
707 expected
708 + interface Square {
709 + kind: "square";
710 + size: number;
711 + }
712 + interface Rectangle {
713 + kind: "rectangle";
714 + width: number;
715 + height: number;
716 + }
717 + interface Circle {
718 + kind: "circle";
719 + radius: number;
720 + }
721 + // First we declare the interfaces we will union. Each interface has a kind property with
722 + // a different string literal type.The kind property is called the discriminant or tag.The other properties are specific to each interface.Notice that the interfaces are currently unrelated.Let’s put them into a union:
723 + type Shape = Square | Rectangle | Circle;
724 + // Now let’s use the discriminated union:
725 + function area(s: Shape) {
726 + switch (s.kind) {
727 + case "square": return s.size * s.size;
728 + case "rectangle": return s.height * s.width;
729 + case "circle": return Math.PI * s.radius ** 2;
730 + }
731 + }
732 ittf
733 +
734 :interface Square
735 :p kind
736 :literal "square"
737 :p size
738 :number
739 :interface Rectangle
740 :p kind
741 :literal "rectangle"
742 :p width
743 :number
744 :p height
745 :number
746 :interface Circle
747 :p kind
748 :literal "circle"
749 :p radius
750 :number
751 :type Shape
752 :union
753 :ref Square
754 :ref Rectangle
755 :ref Circle
756 function area
757 param s
758 :ref Shape
759 switch s.kind
760 case "square"
761 return s.size * s.size
762 case "rectangle"
763 return s.height * s.width
764 case "circle"
765 return Math.PI * s.radius ** 2
766 item
767 title Exhaustiveness checking
768 expected
769 + type Shape = Square | Rectangle | Circle | Triangle;
770 + function area(s: Shape) {
771 + switch (s.kind) {
772 + case "square": return s.size * s.size;
773 + case "rectangle": return s.height * s.width;
774 + case "circle": return Math.PI * s.radius ** 2;
775 + }
776 + // should error here - we didn't handle case "triangle"
777 + }
778 + // There are two ways to do this. The first is to turn on --strictNullChecks and specify a return type:
779 + function area(s: Shape): number { // error: returns number | undefined
780 + switch (s.kind) {
781 + case "square": return s.size * s.size;
782 + case "rectangle": return s.height * s.width;
783 + case "circle": return Math.PI * s.radius ** 2;
784 + }
785 + }
786 + // Because the switch is no longer exhaustive, TypeScript is aware that the function could sometimes
787 + // return undefined.If you have an explicit return type number, then you will get an error that
788 + // the return type is actually number | undefined.However, this method is quite subtle and, besides, --strictNullChecks does not always work with old code.
789 + // The second method uses the never type that the compiler uses to check for exhaustiveness:
790 + function assertNever(x: never): never {
791 + throw new Error("Unexpected object: " + x);
792 + }
793 + function area(s: Shape) {
794 + switch (s.kind) {
795 + case "square": return s.size * s.size;
796 + case "rectangle": return s.height * s.width;
797 + case "circle": return Math.PI * s.radius ** 2;
798 + default: return assertNever(s); // error here if there are missing cases
799 + }
800 + }
801 ittf
802 +
803 :type Shape
804 :union
805 :ref Square
806 :ref Rectangle
807 :ref Circle
808 :ref Triangle
809 function area
810 param s
811 :ref Shape
812 switch s.kind
813 case "square"
814 return s.size * s.size
815 case "rectangle"
816 return s.height * s.width
817 case "circle"
818 return Math.PI * s.radius ** 2
819 function area
820 param s
821 :ref Shape
822 :return
823 :number
824 switch s.kind
825 case "square"
826 return s.size * s.size
827 case "rectangle"
828 return s.height * s.width
829 case "circle"
830 return Math.PI * s.radius ** 2
831 function assertNever
832 param x
833 :never
834 :return
835 :never
836 throw new Error("Unexpected object: " + x)
837 function area
838 param s
839 :ref Shape
840 switch s.kind
841 case "square"
842 return s.size * s.size
843 case "rectangle"
844 return s.height * s.width
845 case "circle"
846 return Math.PI * s.radius ** 2
847 default
848 return assertNever(s)
849 item
850 title Polymorphic this types
851 expected
852 + class BasicCalculator {
853 + public constructor(protected value: number = 0) { }
854 + public currentValue(): number {
855 + return this.value;
856 + }
857 + public add(operand: number): this {
858 + this.value += operand;
859 + return this;
860 + }
861 + public multiply(operand: number): this {
862 + this.value *= operand;
863 + return this;
864 + }
865 + // ... other operations go here ...
866 + }
867 + let v = new BasicCalculator(2)
868 + .multiply(5)
869 + .add(1)
870 + .currentValue();
871 + // Since the class uses this types, you can extend it and the new class can use the old methods with no changes.
872 + class ScientificCalculator extends BasicCalculator {
873 + public constructor(value = 0) {
874 + super(value);
875 + }
876 + public sin() {
877 + this.value = Math.sin(this.value);
878 + return this;
879 + }
880 + // ... other operations go here ...
881 + }
882 + let v = new ScientificCalculator(2)
883 + .multiply(5)
884 + .sin()
885 + .add(1)
886 + .currentValue();
887 + // Without this types, ScientificCalculator would not have been able to extend BasicCalculator and keep the fluent interface. multiply would have returned BasicCalculator, which doesn’t have the sin method. However, with this types, multiply returns this, which is ScientificCalculator here.
888 ittf
889 +
890 class BasicCalculator
891 ctor
892 :public
893 param value
894 :number
895 := 0
896 m currentValue
897 :public
898 return this.value
899 m add
900 :public
901 param operand
902 :number
903 set this.value += operand
904 return this
905 m multiply
906 :public
907 param operand
908 :number
909 set this.value *= operand
910 return this
911 let v = new BasicCalculator(2).multiply(5).add(1).currentValue()
912 class ScientificCalculator
913 super BasicCalculator
914 ctor
915 :public
916 param value = 0
917 _ super(value)
918 m sin
919 :public
920 set this.value = Math.sin(this.value)
921 return this
922 let v = new ScientificCalculator(2).multiply(5).sin().add(1).currentValue()
923 item
924 title Index types
925 expected
926 + function pluck(o, names) {
927 + return names.map(n => o[n]);
928 + }
929 + // Here’s how you would write and use this function in TypeScript, using the index type query and indexed access operators:
930 + function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
931 + return names.map(n => o[n]);
932 + }
933 + interface Person {
934 + name: string;
935 + age: number;
936 + }
937 + let person: Person = {
938 + name: 'Jarid',
939 + age: 35
940 + };
941 + let strings: string[] = pluck(person, ['name']); // ok, string[]
942 + // The compiler checks that name is actually a property on Person. The example introduces a couple of new type operators.
943 + // First is keyof T, the index type query operator.For any type T, keyof T is the union of known, public property names of T.For example:
944 + let personProps: keyof Person; // 'name' | 'age'
945 + // keyof Person is completely interchangeable with 'name' | 'age'. The difference is that if you add another property to Person,
946 + // say address: string, then keyof Person will automatically update to be 'name' | 'age' | 'address'.And you can use keyof in generic
947 + // contexts like pluck, where you can’t possibly know the property names ahead of time.That means the compiler will check that you pass
948 + // the right set of property names to pluck:
949 + pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'
950 + // The second operator is T[K], the indexed access operator. Here, the type syntax reflects the expression syntax.
951 + // That means that person['name'] has the type Person['name']— which in our example is just string.However, just like index type queries,
952 + // you can use T[K] in a generic context, which is where its real power comes to life.You just have to make sure that the type variable K extends keyof T.
953 + // Here’s another example with a function named getProperty.
954 + function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
955 + return o[name]; // o[name] is of type T[K]
956 + }
957 + // In getProperty, o: T and name: K, so that means o[name]: T[K]. Once you return the T[K] result, the compiler will instantiate the actual type of the key,
958 + // so the return type of getProperty will vary according to which property you request.
959 + let name: string = getProperty(person, 'name');
960 + let age: number = getProperty(person, 'age');
961 + let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
962 + // Index types and string index signatures
963 + // keyof and T[K] interact with string index signatures. If you have a type with a string index signature, keyof T will just be string.
964 + // And T[string] is just the type of the index signature:
965 + interface Map<T> {
966 + [key: string]: T;
967 + }
968 + let keys: keyof Map<number>; // string
969 + let value: Map<number>['foo']; // number
970 ittf
971 +
972 function pluck
973 param o
974 param names
975 return
976 _ names.map
977 =>
978 param n
979 + o[n]
980 function pluck
981 :< T
982 :< K
983 :keyof
984 :ref T
985 param o
986 :ref T
987 param names
988 :[
989 :ref K
990 :return
991 :[
992 :[]
993 :ref T
994 :ref K
995 return
996 _ names.map
997 =>
998 param n
999 + o[n]
1000 :interface Person
1001 :p name
1002 :string
1003 :p age
1004 :number
1005 let person
1006 :ref Person
1007 {
1008 @ name 'Jarid'
1009 @ age 35
1010 let strings
1011 :[
1012 :string
1013 _ pluck
1014 @ person
1015 [
1016 @ 'name'
1017 let personProps
1018 :keyof
1019 :ref Person
1020 _ pluck
1021 @ person
1022 [
1023 @ 'age'
1024 @ 'unknown'
1025 function getProperty
1026 :< T
1027 :< K
1028 :keyof
1029 :ref T
1030 param o
1031 :ref T
1032 param name
1033 :ref K
1034 :return
1035 :[]
1036 :ref T
1037 :ref K
1038 return o[name]
1039 let name
1040 :string
1041 _ getProperty(person, 'name')
1042 let age
1043 :number
1044 _ getProperty(person, 'age')
1045 let unknown = getProperty(person, 'unknown')
1046 :interface Map
1047 :< T
1048 :index
1049 :ref T
1050 param key
1051 :string
1052 let keys
1053 :keyof
1054 :ref Map
1055 :number
1056 let value
1057 :[]
1058 :ref Map
1059 :number
1060 :literal 'foo'
1061 item
1062 title Mapped types
1063 expected
1064 + interface PersonPartial {
1065 + name?: string;
1066 + age?: number;
1067 + }
1068 + // Or we might want a readonly version:
1069 + interface PersonReadonly {
1070 + readonly name: string;
1071 + readonly age: number;
1072 + }
1073 + // This happens often enough in Javascript that TypeScript provides a way to create new types based on old types — mapped types.
1074 + // In a mapped type, the new type transforms each property in the old type in the same way.For example, you can make all properties
1075 + // of a type readonly or optional.Here are a couple of examples:
1076 + type Readonly<T> = {
1077 + readonly [P in keyof T]: T[P];
1078 + }
1079 + type Partial<T> = {
1080 + [P in keyof T]?: T[P];
1081 + }
1082 + // And to use it:
1083 + type PersonPartial = Partial<Person>;
1084 + type ReadonlyPerson = Readonly<Person>;
1085 + // Let’s take a look at the simplest mapped type and its parts:
1086 + type Keys = 'option1' | 'option2';
1087 + type Flags = {[K in Keys]: boolean };
1088 + // The syntax resembles the syntax for index signatures with a for .. in inside. There are three parts:
1089 ittf
1090 +
1091 :interface PersonPartial
1092 :p name
1093 :optional
1094 :string
1095 :p age
1096 :optional
1097 :number
1098 :interface PersonReadonly
1099 :p name
1100 :string
1101 :p age
1102 :number
1103 :type Readonly
1104 :< T
1105 :mapped
1106 :< P
1107 :keyof
1108 :ref T
1109 :[]
1110 :ref T
1111 :ref P
1112 :type Partial
1113 :< T
1114 :mapped
1115 :optional
1116 :< P
1117 :keyof
1118 :ref T
1119 :[]
1120 :ref T
1121 :ref P
1122 :type PersonPartial
1123 :ref Partial
1124 :ref Person
1125 :type ReadonlyPerson
1126 :ref Readonly
1127 :ref Person
1128 :type Keys
1129 :union
1130 :literal 'option1'
1131 :literal 'option2'
1132 :type Flags
1133 :mapped
1134 :< K
1135 :ref Keys
1136 :boolean
1137 item
1138 title Mapped types 2
1139 expected
1140 + /*
1141 + The type variable K, which gets bound to each property in turn.
1142 + The string literal union Keys, which contains the names of properties to iterate over.
1143 + The resulting type of the property.
1144 + In this simple example, Keys is a hard-coded list of property names and the property type is always boolean, so this mapped type is equivalent to writing: */
1145 + type Flags = {
1146 + option1: boolean;
1147 + option2: boolean;
1148 + }
1149 + // Real applications, however, look like Readonly or Partial above. They’re based on some existing type, and they transform the properties
1150 + // in some way. That’s where keyof and indexed access types come in:
1151 + type NullablePerson = {[P in keyof Person]: Person[P] | null }
1152 + type PartialPerson = {[P in keyof Person]?: Person[P]}
1153 + // But it’s more useful to have a general version.
1154 + type Nullable<T> = {[P in keyof T]: T[P] | null }
1155 + type Partial<T> = {[P in keyof T]?: T[P]}
1156 + // In these examples, the properties list is keyof T and the resulting type is some variant of T[P].
1157 + // This is a good template for any general use of mapped types. That’s because this kind of transformation is homomorphic,
1158 + // which means that the mapping applies only to properties of T and no others. The compiler knows that it can copy all the existing property
1159 + // modifiers before adding any new ones. For example, if Person.name was readonly, Partial<Person>.name would be readonly and optional.
1160 + // Here’s one more example, in which T[P] is wrapped in a Proxy<T> class:
1161 + type Proxy<T> = {
1162 + get(): T;
1163 + set(value: T): void;
1164 + }
1165 + type Proxify<T> = {
1166 + [P in keyof T]: Proxy<T[P]>;
1167 + }
1168 + function proxify<T>(o: T): Proxify<T> {
1169 + // ... wrap proxies ...
1170 + }
1171 + let proxyProps = proxify(props);
1172 + // Note that Readonly<T> and Partial<T> are so useful, they are included in TypeScript’s standard library along with Pick and Record:
1173 + type Pick<T, K extends keyof T> = {
1174 + [P in K]: T[P];
1175 + }
1176 + type Record<K extends string, T> = {
1177 + [P in K]: T;
1178 + }
1179 + // Readonly, Partial and Pick are homomorphic whereas Record is not. One clue that Record is not homomorphic is that it doesn’t take
1180 + // an input type to copy properties from:
1181 + type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
1182 + // Non-homomorphic types are essentially creating new properties, so they can’t copy property modifiers from anywhere.
1183 ittf
1184 +
1185 :type Flags
1186 :{
1187 :p option1
1188 :boolean
1189 :p option2
1190 :boolean
1191 :type NullablePerson
1192 :mapped
1193 :< P
1194 :keyof
1195 :ref Person
1196 :union
1197 :[]
1198 :ref Person
1199 :ref P
1200 :null
1201 :type PartialPerson
1202 :mapped
1203 :optional
1204 :< P
1205 :keyof
1206 :ref Person
1207 :[]
1208 :ref Person
1209 :ref P
1210 :type Nullable
1211 :< T
1212 :mapped
1213 :< P
1214 :keyof
1215 :ref T
1216 :union
1217 :[]
1218 :ref T
1219 :ref P
1220 :null
1221 :type Partial
1222 :< T
1223 :mapped
1224 :optional
1225 :< P
1226 :keyof
1227 :ref T
1228 :[]
1229 :ref T
1230 :ref P
1231 :type Proxy
1232 :< T
1233 :{
1234 :m get
1235 :ref T
1236 :m set
1237 :void
1238 param value
1239 :ref T
1240 :type Proxify
1241 :< T
1242 :mapped
1243 :< P
1244 :keyof
1245 :ref T
1246 :ref Proxy
1247 :[]
1248 :ref T
1249 :ref P
1250 function proxify
1251 :< T
1252 param o
1253 :ref T
1254 :return
1255 :ref Proxify
1256 :ref T
1257 let proxyProps = proxify(props)
1258 :type Pick
1259 :< T
1260 :< K
1261 :keyof
1262 :ref T
1263 :mapped
1264 :< P
1265 :ref K
1266 :[]
1267 :ref T
1268 :ref P
1269 :type Record
1270 :< K
1271 :string
1272 :< T
1273 :mapped
1274 :< P
1275 :ref K
1276 :ref T
1277 :type ThreeStringProps
1278 :ref Record
1279 :union
1280 :literal 'prop1'
1281 :literal 'prop2'
1282 :literal 'prop3'
1283 :string
1284 item
1285 title Inference from mapped types
1286 expected
1287 + function unproxify<T>(t: Proxify<T>): T {
1288 + let result = {} as T;
1289 + for (const k in t) {
1290 + result[k] = t[k].get();
1291 + }
1292 + return result;
1293 + }
1294 + let originalProps = unproxify(proxyProps);
1295 ittf
1296 +
1297 function unproxify
1298 :< T
1299 param t
1300 :ref Proxify
1301 :param
1302 :ref T
1303 :return
1304 :ref T
1305 let result = {}
1306 for const k in t
1307 set result[k] = t[k].get()
1308 return result
1309 let originalProps = unproxify(proxyProps)
1310 item
1311 title Conditional Types
1312 expected
1313 + declare function f<T extends boolean>(x: T): T extends true ? string : number;
1314 + // Type is 'string | number
1315 + let x = f(Math.random() < 0.5)
1316 + // Another example would be the TypeName type alias, which uses nested conditional types:
1317 + type TypeName<T> =
1318 + T extends string ? "string" :
1319 + T extends number ? "number" :
1320 + T extends boolean ? "boolean" :
1321 + T extends undefined ? "undefined" :
1322 + T extends Function ? "function" :
1323 + "object";
1324 + type T0 = TypeName<string>; // "string"
1325 + type T1 = TypeName<"a">; // "string"
1326 + type T2 = TypeName<true>; // "boolean"
1327 + type T3 = TypeName<() => void>; // "function"
1328 + type T4 = TypeName<string[]>; // "object"
1329 + // But as an example of a place where conditonal types are deferred - where they stick around instead of picking a branch - would be in the following:
1330 + interface Foo {
1331 + propA: boolean;
1332 + propB: boolean;
1333 + }
1334 + declare function f<T>(x: T): T extends Foo ? string : number;
1335 + function foo<U>(x: U) {
1336 + // Has type 'U extends Foo ? string : number'
1337 + let a = f(x);
1338 + // This assignment is allowed though!
1339 + let b: string | number = a;
1340 + }
1341 ittf
1342 +
1343 :function f
1344 :< T
1345 :boolean
1346 param x
1347 :ref T
1348 :return
1349 :iif
1350 :check
1351 :ref T
1352 :extends
1353 :literal true
1354 :then
1355 :string
1356 :else
1357 :number
1358 # Type is 'string | number
1359 let x = f(Math.random() < 0.5)
1360 # Another example would be the TypeName type alias, which uses nested conditional types:
1361 :type TypeName
1362 :< T
1363 :iif
1364 :check
1365 :ref T
1366 :extends
1367 :string
1368 :then
1369 :literal "string"
1370 :else
1371 :iif
1372 :check
1373 :ref T
1374 :extends
1375 :number
1376 :then
1377 :literal "number"
1378 :else
1379 :iif
1380 :check
1381 :ref T
1382 :extends
1383 :boolean
1384 :then
1385 :literal "boolean"
1386 :else
1387 :iif
1388 :check
1389 :ref T
1390 :extends
1391 :void
1392 :then
1393 :literal "undefined"
1394 :else
1395 :iif
1396 :check
1397 :ref T
1398 :extends
1399 :ref Function
1400 :then
1401 :literal "function"
1402 :else
1403 :literal "object"
1404 :type T0
1405 :ref TypeName
1406 :param string
1407 # "string"
1408 :type T1
1409 :ref TypeName
1410 :param
1411 :literal "a"
1412 # "string"
1413 :type T2
1414 :ref TypeName
1415 :param
1416 :literal true
1417 # "boolean"
1418 :type T3
1419 :ref TypeName
1420 :param
1421 :=>
1422 :void
1423 # "function"
1424 :type T4
1425 :ref TypeName
1426 :param
1427 :[
1428 :string
1429 # "object"
1430 # But as an example of a place where conditonal types are deferred - where they stick around instead of picking a branch - would be in the following:
1431 :interface Foo
1432 :p propA
1433 :boolean
1434 :p propB
1435 :boolean
1436 :function f
1437 :< T
1438 param x
1439 :ref T
1440 :return
1441 :iif
1442 :check
1443 :ref T
1444 :extends
1445 :ref Foo
1446 :then
1447 :string
1448 :else
1449 :number
1450 function foo
1451 :< U
1452 param x
1453 :ref U
1454 # Has type 'U extends Foo ? string : number'
1455 let a = f(x)
1456 # This assignment is allowed though!
1457 let b
1458 :union
1459 :string
1460 :number
1461 := a
1462 item
1463 title Distributive conditional types
1464 expected
1465 + type T10 = TypeName<string | (() => void)>; // "string" | "function"
1466 + type T12 = TypeName<string | string[] | undefined>; // "string" | "object" | "undefined"
1467 + type T11 = TypeName<string[] | number[]>; // "object"
1468 + /* In instantiations of a distributive conditional type T extends U ? X : Y, references to T within the conditional type are resolved
1469 + to individual constituents of the union type (i.e. T refers to the individual constituents after the conditional type is distributed
1470 + over the union type). Furthermore, references to T within X have an additional type parameter constraint U (i.e. T is considered
1471 + assignable to U within X).*/
1472 + // Example
1473 + type BoxedValue<T> = { value: T };
1474 + type BoxedArray<T> = { array: T[] };
1475 + type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
1476 + type T20 = Boxed<string>; // BoxedValue<string>;
1477 + type T21 = Boxed<number[]>; // BoxedArray<number>;
1478 + type T22 = Boxed<string | number[]>; // BoxedValue<string> | BoxedArray<number>;
1479 + // Notice that T has the additional constraint any[] within the true branch of Boxed<T> and it is therefore possible to refer to the element type of the array as T[number]. Also, notice how the conditional type is distributed over the union type in the last example.
1480 + // The distributive property of conditional types can conveniently be used to filter union types:
1481 + type Diff<T, U> = T extends U ? never : T; // Remove types from T that are assignable to U
1482 + type Filter<T, U> = T extends U ? T : never; // Remove types from T that are not assignable to U
1483 + type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
1484 + type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
1485 + type T32 = Diff<string | number | (() => void), Function>; // string | number
1486 + type T33 = Filter<string | number | (() => void), Function>; // () => void
1487 + type NonNullable<T> = Diff<T, null | undefined>; // Remove null and undefined from T
1488 + type T34 = NonNullable<string | number | undefined>; // string | number
1489 + type T35 = NonNullable<string | string[] | null | undefined>; // string | string[]
1490 + function f1<T>(x: T, y: NonNullable<T>) {
1491 + x = y; // Ok
1492 + y = x; // Error
1493 + }
1494 + function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
1495 + x = y; // Ok
1496 + y = x; // Error
1497 + let s1: string = x; // Error
1498 + let s2: string = y; // Ok
1499 + }
1500 ittf
1501 +
1502 :type T10
1503 :ref TypeName
1504 :param
1505 :union
1506 :string
1507 :paren
1508 :=>
1509 :void
1510 :type T12
1511 :ref TypeName
1512 :param
1513 :union
1514 :string
1515 :[
1516 :string
1517 :void
1518 :type T11
1519 :ref TypeName
1520 :param
1521 :union
1522 :[
1523 :string
1524 :[
1525 :number
1526 #
1527 # In instantiations of a distributive conditional type T extends U ? X : Y, references to T within the conditional type are resolved
1528 # to individual constituents of the union type (i.e. T refers to the individual constituents after the conditional type is distributed
1529 # over the union type). Furthermore, references to T within X have an additional type parameter constraint U (i.e. T is considered
1530 # assignable to U within X).
1531 :type BoxedValue
1532 :< T
1533 :{
1534 :p value
1535 :ref T
1536 :type BoxedArray
1537 :< T
1538 :{
1539 :p array
1540 :[
1541 :ref T
1542 :type Boxed
1543 :< T
1544 :iif
1545 :check
1546 :ref T
1547 :extends
1548 :[
1549 :any
1550 :then
1551 :ref BoxedArray
1552 :param
1553 :[]
1554 :ref T
1555 :number
1556 :else
1557 :ref BoxedValue
1558 :param
1559 :ref T
1560 :type T20
1561 :ref Boxed
1562 :param string
1563 :type T21
1564 :ref Boxed
1565 :param
1566 :[
1567 :number
1568 :type T22
1569 :ref Boxed
1570 :param
1571 :union
1572 :string
1573 :[
1574 :number
1575 :type Diff
1576 :< T
1577 :< U
1578 :iif
1579 :check
1580 :ref T
1581 :extends
1582 :ref U
1583 :then
1584 :never
1585 :else
1586 :ref T
1587 :type Filter
1588 :< T
1589 :< U
1590 :iif
1591 :check
1592 :ref T
1593 :extends
1594 :ref U
1595 :then
1596 :ref T
1597 :else
1598 :never
1599 :type T30
1600 :ref Diff
1601 :param
1602 :union
1603 :literal "a"
1604 :literal "b"
1605 :literal "c"
1606 :literal "d"
1607 :param
1608 :union
1609 :literal "a"
1610 :literal "c"
1611 :literal "f"
1612 :type T31
1613 :ref Filter
1614 :param
1615 :union
1616 :literal "a"
1617 :literal "b"
1618 :literal "c"
1619 :literal "d"
1620 :param
1621 :union
1622 :literal "a"
1623 :literal "c"
1624 :literal "f"
1625 :type T32
1626 :ref Diff
1627 :param
1628 :union
1629 :string
1630 :number
1631 :paren
1632 :=>
1633 :void
1634 :param
1635 :ref Function
1636 :type T33
1637 :ref Filter
1638 :param
1639 :union
1640 :string
1641 :number
1642 :paren
1643 :=>
1644 :void
1645 :param
1646 :ref Function
1647 :type NonNullable
1648 :< T
1649 :ref Diff
1650 :param
1651 :ref T
1652 :param
1653 :union
1654 :null
1655 :void
1656 :type T34
1657 :ref NonNullable
1658 :param
1659 :union
1660 :string
1661 :number
1662 :void
1663 :type T35
1664 :ref NonNullable
1665 :param
1666 :union
1667 :string
1668 :[
1669 :string
1670 :null
1671 :void
1672 function f1
1673 :< T
1674 param x
1675 :ref T
1676 param y
1677 :ref NonNullable
1678 :param
1679 :ref T
1680 set x = y
1681 set y = x
1682 function f2
1683 :< T
1684 :union
1685 :string
1686 :void
1687 param x
1688 :ref T
1689 param y
1690 :ref NonNullable
1691 :param
1692 :ref T
1693 set x = y
1694 set y = x
1695 let s1
1696 :string
1697 := x
1698 let s2
1699 :string
1700 := y
1701 item
1702 title Conditional types combined with mapped types:
1703 expected
1704 + // conditional types are particularly useful when combined with mapped types:
1705 + type FunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? K : never }[keyof T];
1706 + type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
1707 + type NonFunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? never : K }[keyof T];
1708 + type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
1709 + interface Part {
1710 + id: number;
1711 + name: string;
1712 + subparts: Part[];
1713 + updatePart(newName: string): void;
1714 + }
1715 + type T40 = FunctionPropertyNames<Part>; // "updatePart"
1716 + type T41 = NonFunctionPropertyNames<Part>; // "id" | "name" | "subparts"
1717 + type T42 = FunctionProperties<Part>; // { updatePart(newName: string): void }
1718 + type T43 = NonFunctionProperties<Part>; // { id: number, name: string, subparts: Part[] }
1719 + // Similar to union and intersection types, conditional types are not permitted to reference themselves recursively.For example the following is an error.
1720 + // Example
1721 + type ElementType<T> = T extends any[] ? ElementType<T[number]> : T; // Error
1722 ittf
1723 +
1724 :type FunctionPropertyNames
1725 :< T
1726 :[]
1727 :mapped
1728 :< K
1729 :keyof
1730 :ref T
1731 :iif
1732 :check
1733 :[]
1734 :ref T
1735 :ref K
1736 :extends
1737 :ref Function
1738 :then
1739 :ref K
1740 :else
1741 :never
1742 :keyof
1743 :ref T
1744 :type FunctionProperties
1745 :< T
1746 :ref Pick
1747 :param
1748 :ref T
1749 :param
1750 :ref FunctionPropertyNames
1751 :param
1752 :ref T
1753 :type NonFunctionPropertyNames
1754 :< T
1755 :[]
1756 :mapped
1757 :< K
1758 :keyof
1759 :ref T
1760 :iif
1761 :check
1762 :[]
1763 :ref T
1764 :ref K
1765 :extends
1766 :ref Function
1767 :then
1768 :never
1769 :else
1770 :ref K
1771 :keyof
1772 :ref T
1773 :type NonFunctionProperties
1774 :< T
1775 :ref Pick
1776 :param
1777 :ref T
1778 :param
1779 :ref NonFunctionPropertyNames
1780 :param
1781 :ref T
1782 :interface Part
1783 :p id
1784 :number
1785 :p name
1786 :string
1787 :p subparts
1788 :[
1789 :ref Part
1790 :m updatePart
1791 :void
1792 param newName
1793 :string
1794 :type T40
1795 :ref FunctionPropertyNames
1796 :param
1797 :ref Part
1798 :type T41
1799 :ref NonFunctionPropertyNames
1800 :param
1801 :ref Part
1802 :type T42
1803 :ref FunctionProperties
1804 :param
1805 :ref Part
1806 :type T43
1807 :ref NonFunctionProperties
1808 :param
1809 :ref Part
1810 :type ElementType
1811 :< T
1812 :iif
1813 :check
1814 :ref T
1815 :extends
1816 :[
1817 :any
1818 :then
1819 :ref ElementType
1820 :param
1821 :[]
1822 :ref T
1823 :number
1824 :else
1825 :ref T
1826 item
1827 title Type inference in conditional types
1828 expected
1829 + //For example, the following extracts the return type of a function type:
1830 + type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
1831 + // Conditional types can be nested to form a sequence of pattern matches that are evaluated in order:
1832 + type Unpacked<T> =
1833 + T extends (infer U)[] ? U :
1834 + T extends (...args: any[]) => infer U ? U :
1835 + T extends Promise < infer U> ? U :
1836 + T;
1837 + type T0 = Unpacked<string>; // string
1838 + type T1 = Unpacked<string[]>; // string
1839 + type T2 = Unpacked<() => string>; // string
1840 + type T3 = Unpacked<Promise<string>>; // string
1841 + type T4 = Unpacked<Promise<string>[]>; // Promise<string>
1842 + type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
1843 + // The following example demonstrates how multiple candidates for the same type variable in co - variant positions causes a union type to be inferred:
1844 + type Foo<T> = T extends { a: infer U, b: infer U } ? U: never;
1845 + type T10 = Foo<{ a: string, b: string }>; // string
1846 + type T11 = Foo<{ a: string, b: number }>; // string | number
1847 + // Likewise, multiple candidates for the same type variable in contra - variant positions causes an intersection type to be inferred:
1848 + type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U: never;
1849 + type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string
1850 + type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number
1851 + // When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made
1852 + // from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload
1853 + // resolution based on a list of argument types.
1854 + declare function foo(x: string): number;
1855 + declare function foo(x: number): string;
1856 + declare function foo(x: string | number): string | number;
1857 + type T30 = ReturnType<typeof foo>; // string | number
1858 + // It is not possible to use infer declarations in constraint clauses for regular type parameters:
1859 + type ReturnType<T extends (...args: any[]) => infer R> = R; // Error, not supported
1860 + // However, much the same effect can be obtained by erasing the type variables in the constraint and instead specifying a conditional type:
1861 + type AnyFunction = (...args: any[]) => any;
1862 + type ReturnType<T extends AnyFunction> = T extends (...args: any[]) => infer R ? R : any;
1863 ittf
1864 +
1865 :type ReturnType
1866 :< T
1867 :iif
1868 :check
1869 :ref T
1870 :extends
1871 :=>
1872 :infer
1873 :< R
1874 param ...args
1875 :[
1876 :any
1877 :then
1878 :ref R
1879 :else
1880 :any
1881 :type Unpacked
1882 :< T
1883 :iif
1884 :check
1885 :ref T
1886 :extends
1887 :[
1888 :paren
1889 :infer
1890 :< U
1891 :then
1892 :ref U
1893 :else
1894 :iif
1895 :check
1896 :ref T
1897 :extends
1898 :=>
1899 :infer
1900 :< U
1901 param ...args
1902 :[
1903 :any
1904 :then
1905 :ref U
1906 :else
1907 :iif
1908 :check
1909 :ref T
1910 :extends
1911 :ref Promise
1912 :param
1913 :infer
1914 :< U
1915 :then
1916 :ref U
1917 :else
1918 :ref T
1919 :type T0
1920 :ref Unpacked
1921 :param string
1922 :type T1
1923 :ref Unpacked
1924 :param
1925 :[
1926 :string
1927 :type T2
1928 :ref Unpacked
1929 :param
1930 :=>
1931 :string
1932 :type T3
1933 :ref Unpacked
1934 :param
1935 :ref Promise
1936 :param string
1937 :type T4
1938 :ref Unpacked
1939 :param
1940 :[
1941 :ref Promise
1942 :param string
1943 :type T5
1944 :ref Unpacked
1945 :param
1946 :ref Unpacked
1947 :param
1948 :[
1949 :ref Promise
1950 :param string
1951 :type Foo
1952 :< T
1953 :iif
1954 :check
1955 :ref T
1956 :extends
1957 :{
1958 :p a
1959 :infer
1960 :< U
1961 :p b
1962 :infer
1963 :< U
1964 :then
1965 :ref U
1966 :else
1967 :never
1968 :type T10
1969 :ref Foo
1970 :param
1971 :{
1972 :p a
1973 :string
1974 :p b
1975 :string
1976 :type T11
1977 :ref Foo
1978 :param
1979 :{
1980 :p a
1981 :string
1982 :p b
1983 :number
1984 :type Bar
1985 :< T
1986 :iif
1987 :check
1988 :ref T
1989 :extends
1990 :{
1991 :p a
1992 :=>
1993 :void
1994 param x
1995 :infer
1996 :< U
1997 :p b
1998 :=>
1999 :void
2000 param x
2001 :infer
2002 :< U
2003 :then
2004 :ref U
2005 :else
2006 :never
2007 :type T20
2008 :ref Bar
2009 :param
2010 :{
2011 :p a
2012 :=>
2013 :void
2014 param x
2015 :string
2016 :p b
2017 :=>
2018 :void
2019 param x
2020 :string
2021 :type T21
2022 :ref Bar
2023 :param
2024 :{
2025 :p a
2026 :=>
2027 :void
2028 param x
2029 :string
2030 :p b
2031 :=>
2032 :void
2033 param x
2034 :number
2035 :function foo
2036 param x
2037 :string
2038 :return
2039 :number
2040 :function foo
2041 param x
2042 :number
2043 :return
2044 :string
2045 :function foo
2046 param x
2047 :union
2048 :string
2049 :number
2050 :return
2051 :union
2052 :string
2053 :number
2054 :type T30
2055 :ref ReturnType
2056 :param
2057 :typeof foo
2058 :type ReturnType
2059 :< T
2060 :=>
2061 :infer
2062 :< R
2063 param ...args
2064 :[
2065 :any
2066 :ref R
2067 :type AnyFunction
2068 :=>
2069 :any
2070 param ...args
2071 :[
2072 :any
2073 :type ReturnType
2074 :< T
2075 :ref AnyFunction
2076 :iif
2077 :check
2078 :ref T
2079 :extends
2080 :=>
2081 :infer
2082 :< R
2083 param ...args
2084 :[
2085 :any
2086 :then
2087 :ref R
2088 :else
2089 :any
2090 item
2091 title Predefined conditional types
2092 expected
2093 + type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
2094 + type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
2095 + type T02 = Exclude<string | number | (() => void), Function>; // string | number
2096 + type T03 = Extract<string | number | (() => void), Function>; // () => void
2097 + type T04 = NonNullable<string | number | undefined>; // string | number
2098 + type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
2099 + function f1(s: string) {
2100 + return { a: 1, b: s }
2101 + }
2102 + class C {
2103 + x = 0;
2104 + y = 0;
2105 + }
2106 + type T10 = ReturnType<() => string>; // string
2107 + type T11 = ReturnType<(s: string) => void>; // void
2108 + type T12 = ReturnType<(<T>() => T)>; // {}
2109 + type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
2110 + type T14 = ReturnType<typeof f1>; // { a: number, b: string }
2111 + type T15 = ReturnType<any>; // any
2112 + type T16 = ReturnType<never>; // any
2113 + type T17 = ReturnType<string>; // Error
2114 + type T18 = ReturnType<Function>; // Error
2115 + type T20 = InstanceType<typeof C>; // C
2116 + type T21 = InstanceType<any>; // any
2117 + type T22 = InstanceType<never>; // any
2118 + type T23 = InstanceType<string>; // Error
2119 + type T24 = InstanceType<Function>; // Error
2120 ittf
2121 +
2122 :type T00
2123 :ref Exclude
2124 :param
2125 :union
2126 :literal "a"
2127 :literal "b"
2128 :literal "c"
2129 :literal "d"
2130 :param
2131 :union
2132 :literal "a"
2133 :literal "c"
2134 :literal "f"
2135 :type T01
2136 :ref Extract
2137 :param
2138 :union
2139 :literal "a"
2140 :literal "b"
2141 :literal "c"
2142 :literal "d"
2143 :param
2144 :union
2145 :literal "a"
2146 :literal "c"
2147 :literal "f"
2148 :type T02
2149 :ref Exclude
2150 :param
2151 :union
2152 :string
2153 :number
2154 :paren
2155 :=>
2156 :void
2157 :param
2158 :ref Function
2159 :type T03
2160 :ref Extract
2161 :param
2162 :union
2163 :string
2164 :number
2165 :paren
2166 :=>
2167 :void
2168 :param
2169 :ref Function
2170 :type T04
2171 :ref NonNullable
2172 :param
2173 :union
2174 :string
2175 :number
2176 :void
2177 :type T05
2178 :ref NonNullable
2179 :param
2180 :union
2181 :paren
2182 :=>
2183 :string
2184 :[
2185 :string
2186 :null
2187 :void
2188 function f1
2189 param s
2190 :string
2191 return
2192 {
2193 @ a 1
2194 @ b s
2195 class C
2196 p x
2197 := 0
2198 p y
2199 := 0
2200 :type T10
2201 :ref ReturnType
2202 :param
2203 :=>
2204 :string
2205 :type T11
2206 :ref ReturnType
2207 :param
2208 :=>
2209 :void
2210 param s
2211 :string
2212 :type T12
2213 :ref ReturnType
2214 :param
2215 :paren
2216 :=>
2217 :< T
2218 :ref T
2219 :type T13
2220 :ref ReturnType
2221 :param
2222 :paren
2223 :=>
2224 :< T
2225 :ref U
2226 :< U
2227 :[
2228 :number
2229 :ref T
2230 :type T14
2231 :ref ReturnType
2232 :param
2233 :typeof f1
2234 :type T15
2235 :ref ReturnType
2236 :param any
2237 :type T16
2238 :ref ReturnType
2239 :param never
2240 :type T17
2241 :ref ReturnType
2242 :param string
2243 :type T18
2244 :ref ReturnType
2245 :param
2246 :ref Function
2247 :type T20
2248 :ref InstanceType
2249 :param
2250 :typeof C
2251 :type T21
2252 :ref InstanceType
2253 :param any
2254 :type T22
2255 :ref InstanceType
2256 :param never
2257 :type T23
2258 :ref InstanceType
2259 :param string
2260 :type T24
2261 :ref InstanceType
2262 :param
2263 :ref Function
2264 # Error