Skip to content

Instantly share code, notes, and snippets.

@hachibeeDI
Last active April 24, 2019 04:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hachibeeDI/8a1e29fc7d987b902eae22a161947559 to your computer and use it in GitHub Desktop.
Save hachibeeDI/8a1e29fc7d987b902eae22a161947559 to your computer and use it in GitHub Desktop.

unknown

unknown is safe version of any.

We should consider unknowon first then any.

Union types

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
  }
}

Type guard

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.

https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types

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 for Object

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
    }
  }
}

keyof T

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 member

Generics type narrowing

Generics 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.

Index types

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]);
}

Tuple

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;
}

Overload

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment