You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
Copy file name to clipboardExpand all lines: pages/Advanced Types.md
+213
Original file line number
Diff line number
Diff line change
@@ -568,3 +568,216 @@ let v = new ScientificCalculator(2)
568
568
Without `this` types, `ScientificCalculator` would not have been able to extend `BasicCalculator` and keep the fluent interface.
569
569
`multiply` would have returned `BasicCalculator`, which doesn't have the `sin` method.
570
570
However, with `this` types, `multiply` returns `this`, which is `ScientificCalculator` here.
571
+
572
+
# Index types
573
+
574
+
With index types, you can get the compiler to check code that uses dynamic property names.
575
+
For example, a common Javascript pattern is to pick a subset of properties from an object:
576
+
577
+
```js
578
+
functionpluck(o, names) {
579
+
returnnames.map(n=> o[n]);
580
+
}
581
+
```
582
+
583
+
Here's how you would write and use this function in TypeScript, using the **index type query** and **indexed access** operators:
584
+
585
+
```ts
586
+
function pluck<T, KextendskeyofT>(o:T, names:K[]):T[K][] {
587
+
returnnames.map(n=>o[n]);
588
+
}
589
+
590
+
interfacePerson {
591
+
name:string;
592
+
age:number;
593
+
}
594
+
let person:Person;
595
+
let strings:string[] =pluck(person, ['name']); // ok, string[]
596
+
```
597
+
598
+
The compiler checks that `name` is actually a property on `Person`, and it knows that `strings` is a `string[]` because `name` is a `string`.
599
+
To make this work, the example introduces a couple of new type operators.
600
+
First is `keyof T`, the **index type query operator**.
601
+
For any type `T`, `keyof T` is the union of known, public property names of `T`.
602
+
For example:
603
+
604
+
```ts
605
+
let personProps:keyofPerson; // 'name' | 'age'
606
+
```
607
+
608
+
`keyof Person` is completely interchangeable with `'name' | 'age'`.
609
+
The difference is that if you add another property to `Person`, say `address: string`, then `keyof Person` will automatically update to be `'name' | 'age' | 'address'`.
610
+
And you can use `keyof` in generic contexts like `pluck`, where you can't possibly know the property names ahead of time.
611
+
That means the compiler will check that you pass the right set of property names to `pluck`:
612
+
613
+
```ts
614
+
pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'
615
+
```
616
+
617
+
The second operator is `T[K]`, the **indexed access operator**.
618
+
Here, the type syntax reflects the expression syntax.
619
+
That means that `person['name']` has the type `Person['name']`— which in our example is just `string`.
620
+
However, just like index type queries, you can use `T[K]` in a generic context, which is where its real power comes to life.
621
+
You just have to make sure that the type variable `K extends keyof T`.
622
+
Here's another example with a function named `getProperty`.
623
+
624
+
```ts
625
+
function getProperty<T, KextendskeyofT>(o:T, name:K):T[K] {
626
+
returno[name]; // o[name] is of type T[K]
627
+
}
628
+
```
629
+
630
+
In `getProperty`, `o: T` and `name: K`, so that means `o[name]: T[K]`.
631
+
Once you return the T[K] result, the compiler will instantiate the actual type of the key, so the return type of `getProperty` will vary according to which property you request.
632
+
633
+
```ts
634
+
let name:string=getProperty(person, 'name');
635
+
let age:number=getProperty(person, 'age');
636
+
let unknown =getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
637
+
```
638
+
639
+
## Index types and string index signatures
640
+
641
+
`keyof` and `T[K]` interact with string index signatures.
642
+
If you have a type with a string index signature, `keyof T` will just be `string`.
643
+
And `T[string]` is just the type of the index signature:
644
+
645
+
```ts
646
+
interfaceMap<T> {
647
+
[key:string]:T;
648
+
}
649
+
let keys:keyofMap<number>; // string
650
+
let value:Map<number>['foo']; // number
651
+
```
652
+
653
+
# Mapped types
654
+
655
+
A common task is to take an existing type and make each of its properties optional:
656
+
657
+
```ts
658
+
interfacePersonPartial {
659
+
name?:string;
660
+
age?:number;
661
+
}
662
+
```
663
+
664
+
Or we might want a readonly version:
665
+
666
+
```ts
667
+
interfacePersonReadonly {
668
+
readonly name:string;
669
+
readonly age:number;
670
+
}
671
+
```
672
+
673
+
This happens often enough in Javascript that TypeScript provides a way to create new types based on old types —**mapped types**.
674
+
In a mapped type, the new type transforms each property in the old type in the same way.
675
+
For example, you can make all properties of a type `readonly` or optional.
676
+
Here are a couple of examples:
677
+
678
+
```ts
679
+
typeReadonly<T> = {
680
+
readonly [PinkeyofT]:T[P];
681
+
}
682
+
typePartial<T> = {
683
+
[PinkeyofT]?:T[P];
684
+
}
685
+
```
686
+
687
+
And to use it:
688
+
689
+
```ts
690
+
typePersonPartial=Partial<Person>;
691
+
typeReadonlyPerson=Readonly<Person>;
692
+
```
693
+
694
+
Let's take a look at the simplest mapped type and its parts:
695
+
696
+
```ts
697
+
typeKeys='option1'|'option2';
698
+
typeFlags= { [KinKeys]:boolean };
699
+
```
700
+
701
+
The syntax resembles the syntax for index signatures with a `for .. in` inside.
702
+
There are three parts:
703
+
704
+
1. The type variable `K`, which gets bound to each property in turn.
705
+
2. The string literal union `Keys`, which contains the names of properties to iterate over.
706
+
3. The resulting type of the property.
707
+
708
+
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:
709
+
710
+
```ts
711
+
typeFlags= {
712
+
option1:boolean;
713
+
option2:boolean;
714
+
}
715
+
```
716
+
717
+
Real applications, however, look like `Readonly` or `Partial` above.
718
+
They're based on some existing type, and they transform the fields in some way.
719
+
That's where `keyof` and indexed access types come in:
0 commit comments