unknown is safe version of any.
We should consider unknowon first then any.
When would like to take multiple type as argument.
function isNumeric(target: string | number | null) {
if (target === null) {
return false;
}
if (typeof target === 'number') {
return true;
}
else if (typeof target === 'string') {
// super cool logic to detect if the text is numerical
}
}When you took union types, you would like to detect the value is exactly what of those. (like pattern match)
For undefined and null, you can just compare like x === null.
For native type, you can use typeof for type guarding.
You can see the example for those in above.
For the types you create, you have to write a specific function for that.
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as any as Fish).swim !== undefined;
}Be aware, you can be wrong when writing type guard function.
TIP:
In TypeScript definition, Array.prototype.filter supports type guard. So you can use it to filter union typed array.
const fishAndBirds = [/* ... */];
const fish = fishAndBirds.filter(isFish); // you can also write directly `fishAndBirds.filter(forb: forb is Fish => ...`Type guard is also useful for union typed object. It means you can use it for FSA styled redux action. 👏
type Actions =
| {type: 'user/create' as const, payload: User}
| {type: 'user/delete' as const, payload: never}
| {type: 'user/create/error' as const, payload: Error};
function reducer(state: State, action: Actions) {
switch (action.type) {
case 'user/create': {
console.log(action.payload.name); // this is vaild. compiler can infer payload is a User
}
case 'user/delete': {
console.log(action.payload.name); // this is going to be invalid because compiler infered payload is never
}
}
}When you write a function which take object and field to access the object.
function getterSample<T>(target: T, field: keyof T) {
return target[field];
}
const targetA = {a: 12, b: 13};
getterSample(targetA, 'a');
getterSample(targetA, 'c'); // compile error because targetA has no 'c' as a memberGenerics type argument can be anything besically. However in case we should expect it is following some definition.
I'm introducing example in the section of Tuple.
TypeScript compiler allows to get field value from the key of Object.
https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}If you'd like bring multiple values as Array but the length is fixed, you should use Tuple instead of Array. Actually both of them are same in runtime.
function getTwo<T, F1 extends keyof T, F2 extends keyof T>(target: T, fields: [F1, F2]): [T[F1], T[F2]] {
const [f1, f2] = fields;
return [target[f1], target[f2]] as const;
}TypeScript allows to declare overload function.
So the getTwo can be more reusable but statically typed.
function getMultiple<T, F1 extends keyof T>(target: T, fields: [F1]): [T[F1]];
function getMultiple<T, F1 extends keyof T, F2 extends keyof T>(target: T, fields: [F1, F2]): [T[F1], T[F2]];
function getMultiple<T, F1 extends keyof T, F2 extends keyof T, F3 extends keyof T>(target: T, fields: [F1, F2, F3]): [T[F1], T[F2], T[F3]];
// ...
function getMultiple<T, /* implement getMultiple which follows all of typing of above. May be you can use any. */ {
return foo;
}