So let's squeeze some juice from Ts Mapped types
Index type query operator keyof T
Index type query operator keyof T
provides you the union of public property names of T
.
type OptionKeys = 'option1' | 'option2' // union of possible options
type OptionFlags = { [K in Keys]: boolean }
OptionFlags
is equivalent yo the following type:
type OptionFlags = {
option1: boolean;
option2: boolean;
}
Now let's use keyof T
to produce OptionKeys
out of it:
type OptionKeys = keyof OptionFlags // same as union 'option1' | 'option2'
We could introduce generic for any T
:
type KeysUnion<T> = keyof T;
type OptionKeys = KeysUnion<OptionFlags> // same as union 'option1' | 'option2'
Imagine we're expecting some option change now:
let selectedOption: KeysUnion<OptionFlags> = 'option1'; // OK
selectedOption = 'unknownOption' // Err: Type "unknownOption" is not assignable to type "'option1' | 'option2'"
Describe type with index type typeof keyof T
If we have already instantiated object or enum
we could use typeof
to describe type.
const options = {
option1: true,
option2: false
}
type OptionKeys = keyof typeof options;
let selectedOption: OptionKeys = 'option2';
selectedOption = 'unknownOption' // Err: Type "unknownOption" is not assignable to type "'option1' | 'option2'"
Mapped Types
Transform the properties of existing type in some way to improve your code. You could find the syntax here Advanced Types – mapped types
Note: Following operators are homomorphic – the compiler copies all the existing property modifiers before adding any new ones.
Partial<T>
– make all properties optional
type Partial<T> = {
[P in keyof T]?: T[P];
}
Very handy with incremental updates.
type OptionsUpdate = Partial<Options>
// {
// option1?: boolean;
// option2?: boolean;
// }
Incremental update:
const update: Partial<Options> = {
option2: true
}
const currentOptions: Options = { ...defaults, ...update }; // OK
Required<T>
– make all properties required
type Required<T> = {
[P in keyof T]-?: T[P];
}
Opposite to Partial<T>
– Required<T>
is handy if you need to make all options mandatory.
type OptionsDefault = Required<Options>
// {
// option1: boolean;
// option2: boolean;
// }
Compiler help example:
const defaults: Required<Options> = { // Error: property 'option2' is missing
option1: true
}
Readonly<T>
– make all properties readonly (immutable)
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
----
Immutability is the brother of your code safety.
type ImmutableOptions = Readonly<Options>
// {
// readonly option1: boolean;
// readonly option2: boolean;
// }
Compiler help example:
const snapshot: Readonly<Options> = {
option1: true,
option2: false
}
snapshot.option2 = true; // Error: Cannot assign to 'option2' because it is a read-only property
Nullable<T>
– make all properties nullable
type Nullable<T> = {
[P in keyof T]: T[P] | null
}
type OptionLabels = Nullable<StringOptions>
// {
// option1: string | null;
// option2: string | null;
// }
Compiler help example:
const snapshot: Nullable<StringOptions> = {
option1: 'priority',
option2: 'on'
}
snapshot.option2 = 0; // Error: 0 is not assignable to type 'string | null'
Mutable<T>
– make all properties mutable
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
}
I recommend to not convert any immutable stuff to mutable, yet it still might be useful in some low level optimizations.
Pick<T,K>
– picks the set K
of properties from T
So it narrows the type to the properties we pick:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
Useful for full items/cards preview sub types.
interface Todo {
title: string;
description: string;
priority: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>;
// {
// title: string;
// completed: boolean;
// }
You could achieve the same result via opposite type Omit<T,U>
.
Which picking all properties from T
and then removes set of properties K
.
type TodoPreview = Omit<Todo, 'description'>;
NonNullable<T>
– excludes null
and undefined
from T
type AvailableOption = NonNullable<string | number | null | undefined>; // string | number
Conclusion
The better type declarations you have the safer you are in your development and consequently in production.
Bonus
Describe type from String Enum with [P in T]
It's frequent case when particular features might be turned on/off
and we need to know them all but at the same time to be able to check each one by name.
enum Feature {
Multiplayer = 'multiplayer',
Autosave = 'autosave'
}
type Capabilities = {
readonly [P in Feature]?: boolean;
}
const obj: Capabilities = {
multiplayer: true,
autosave: true,
unknown: false // Error: 'unknown' does not exist in type 'Capabilities'
}
Describe Union type from Array of Strings / String Enum values
const CryptoCodes = <const>['BTC', 'ETH', 'LTC', 'DOGE']
type CryptoCode = typeof CryptoCodes[number]; // same as union 'BTC'|'ETH'|'LTC'|'DOGE'
Thanks for Sharing this Information. AngularJS Training in Gurgaon
ReplyDeleteGreat, thanks for sharing this post.Much thanks again. Awesome.
ReplyDeleter programming training
splunk training