{TypeScript}

Some useful parts

  • Generic Type
  • Keyof Type Operator
  • Conditional Type
  • Indexed Access Types
  • Mapped Type
  • Opaque
  • Simplify / CamelCase (by-time)

Let's Go

Generic Type

// general
const list1: number[] = [1, 2, 3];
// use Generic
const list2: Array<number> = [1, 2, 3]; 
# PRESENTING CODE

Types which take parameters

What is Generic Type?

Generic Type

function returnNumber(arg: number): number {
	return arg;
}


function returnString(arg: string): string {
	return arg;
}


function identity<T>(arg: T): T {
	return arg;
}

let output = identity<string>("myString"); // output: string

let output = identity("myString"); // output: string by type argument inference
# PRESENTING CODE

Generic Constraints

// How to assign type for getLength
function getLength(arg) { // Parameter 'arg' implicitly has an 'any' type,
  return arg.length;
}

function getLength<T>(arg: T): number {
  return arg.length;  // error: Property 'length' does not exist on type 'T'.
}


interface Lengthwise {
  length: number;
}

function getLength<T extends Lengthwise>(arg: T): number {
  return arg.length;
}


getLength(3); // error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.
getLength('abc'); // ✅
getLength([]); // ✅
getLength({ length: 1, value: 3 }); // ✅
# PRESENTING CODE

extends can limit your Types

Keyof Type Operator

type Address = {
  street: string;
  city: string;
  country: string;
};

const keyAbc: keyof Address = 'a'; // error: Type '"a"' is not assignable to type 'keyof Address'.

// keyof Address is "street" | "city" | "country"
const keyCity: keyof Address = 'city'; // ✅
# PRESENTING CODE

1st Demo of creating Types from Types

function prop(obj, key) {
  return obj[key];
}

prop({ city: 'Taipei', country: 'Taiwan' }, 'city'); // return "Taipei"
prop({ city: 'Taipei', country: 'Taiwan' }, 'csity'); // no any hint for Developer typo

function prop<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}

prop({ city: 'Taipei', country: 'Taiwan' }, 'city'); // return "Taipei"
prop({ city: 'Taipei', country: 'Taiwan' }, 'csity'); 
// error: Argument of type '"csity"' is not assignable to parameter of type '"city" | "country"'.
# PRESENTING CODE

Conditional Type

type UserEmailInput = string | null | undefined;

// type UserEmail = string
type UserEmail = NonNullable<UserEmailInput>;
# PRESENTING CODE

Types which act like if statements in the type system

What is Conditional Type?

Conditional Type

# PRESENTING CODE

type on the left of the extends (T) is assignable to the one on the right (U) or not?

T extends U ? X : Y;

Conditional Type

type Foo = null extends null | undefined ? string : number;
// type Foo is string


type Bar<Type> = Type extends null | undefined ? string : number;
type Qoo1 = Bar<null>; // string
type Qoo2 = Bar<boolean>; // number
type Qoo3 = Bar<any>; // ?
# PRESENTING CODE

So what?

null is able to assign to null or undefined, so type Foo and Qoo1 is a string type.
boolean is not able to assign to null or undefined, so type Qoo2 is a number type

2nd Demo of creating Types from Types

type UserEmailInput = string | null | undefined;
type NonNullable<T> = T extends null | undefined ? never : T;

type UserEmail = NonNullable<UserEmailInput>;

// equal to expand type alias UserEmailInput
type UserEmail = NonNullable<string | null | undefined>;

// equal to expand type unions
type UserEmail =
  | NonNullable<string>
  | NonNullable<null>
  | NonNullable<undefined>;

// equal to expand type alias NonNullable
type UserEmail =
  | (string extends null | undefined ? never : string)
  | (null extends null | undefined ? never : null)
  | (undefined extends null | undefined ? never : undefined);

// equal to
type UserEmail = string | never | never ;

// equal to (never is a subtype of all types, so we can omit it here)
type UserEmail = string;


# PRESENTING CODE

Indexed Access Types

type Address = {
  street: string;
  city: string;
  country: string;
};

type City = Address['city']; // City is a string type

type I1 = Address["street" | "city"];
type I2 = Address[keyof Address];     


type Movies = string[];

type FavoriteMovie = Movies[number]; // FavoriteMovie is a string type

type Age = MovieStarList[number]["age"];
# PRESENTING CODE

Mapped Type

type FeatureFlags = {
  darkMode: () => void;
  newUserProfile: () => void;
};
 
type FeatureOptions = OptionsFlags<FeatureFlags>;

// equal to 
type FeatureOptions = {
    darkMode: boolean;
    newUserProfile: boolean;
}
# PRESENTING CODE

Creating types by mapping each property in an existing type

What is Mapped Type?

Mapped Type

type FeatureFlags = {
  darkMode: () => void;
  newUserProfile: () => void;
};
 
type FeatureOptions = OptionsFlags<FeatureFlags>;


type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
};


// equal to 
type FeatureOptions = {
    darkMode: boolean;
    newUserProfile: boolean;
}
# PRESENTING CODE

Mapped Type

type Concrete<Type> = {
  [Property in keyof Type]-?: Type[Property];
};
 
type MaybeUser = {
  id: string;
  name?: string;
  age?: number;
};
 
type User = Concrete<MaybeUser>;
      

// equal to
type User = {
  id: string;
  name: string;
  age: number;
}
# PRESENTING CODE

Creating Types from Types - Simplify

# PRESENTING CODE

Give you a whole view of your nested object

Creating Types from Types - Simplify

type Simplify<T> = T extends Record<string, any> 
  ? { 
      [K in keyof T]: Simplify<T[K]> 
    }
  : T;
# PRESENTING CODE

1. When T is not an object, display it

2. When T is an object, every Key and Value of this object...

3. type of Value should call Simplify
    (call recursively) 

Opaque

// reference: https://michalzalecki.com/nominal-typing-in-typescript/
type Brand<K, T> = K & { __brand: T }

type USD = Brand<number, "USD">
type EUR = Brand<number, "EUR">

const usd = 10 as USD;
const eur = 10 as EUR;

function gross(net: USD, tax: USD): USD {
  return (net + tax) as USD;
}

gross(usd, usd); // ok
gross(eur, usd); // Type '"EUR"' is not assignable to type '"USD"'.
# PRESENTING CODE

Reference:

Code

By 陳Ken

Code

  • 328