# Type Manipulation

🍞

TIP

we actually have a wide variety of type operators available to use. It’s also possible to express types in terms of values that we already have

# Generics

  • Types which take parameters
function loggingIdentity<Type>(arg: Type[]): Type[] {
  console.log(arg.length);
  return arg;
}
let myIdentity: <Type>(arg: Type) => Type = loggingIdentity;
// class
class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}
 
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};

# Type Operator

  • Keyof
  • hhh, just get keys
type Point = { x_YIKI: number; y: number };
type P = keyof Point; // x_YIKI | y
const test : P = "x_YIKI"; // must the same
  • If the type has a string or number index signature, keyof will return those types instead:
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; 
let t : A = 12;
  • ohters
interface Person {
  name: string;
  age: number;
  location: string;
}
type K1 = keyof Person;                   // "name" | "age" | "location"
type K2 = keyof Person[];                 // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

  • Typeof
  • you can use it in a type context to refer to the type of a variable or property
function f() {
  return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>;
let test : P = { x: 11, y: 13};

# Indexed[] Access Types

  • Using Type['a'] syntax to access a subset of a type
  • Using an indexed access type to look up a specific property on another type
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];

# Conditional Types

  • Types which act like if statements in the type system
  • help describe the relation between the types of inputs and outputs

SomeType extends OtherType ? TrueType : FalseType;

interface Animal {}
interface Dog extends Animal {}
type Example1 = Dog extends Animal ? number : string;
type Example2 = RegExp extends Animal ? number : string;
  • When conditional types act on a generic type, they become distributive when given a union type
type ToArray<Type> = Type extends any ? Type[] : never;
  • Distributive
    • When conditional types act on a generic type, they become distributive when given a union type
type ToArray<Type> = Type extends any ? Type[] : never;
type p = ToArray<string | number>; // StrArrOrNumArr = string[] | number[]
const test1 : p = [1,3,'test'] // error
const test2 : p = [1,3] // ok
const test3 : p = ['1','3','test'] // ok
  • To avoid that behavior, surround each side of the extends keyword with [square brackets]
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
type p2 = ToArrayNonDist<string | number>; // (string | number)[]
const test1 : StrArrOrNumArr = [1,3,'test'] // ok

# infer

  • always with extends and in the conditionnal type's true branch
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
  • Conditional types provide us with a way to infer from types we compare against in the true branch using the infer keyword
type GetReturnType<Type> = Type extends 
(...args: never[]) => infer p ? // match `() => any` ? return type p : type nerver
p : never;  
type Num = GetReturnType<() => number>; 
type Str = GetReturnType<(x: string) => string>;
type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
let test : Bools = [true,false];
type nevers = GetReturnType<string>;
let test2 : nevers = (() => { // test2 : never
  throw new Error('Throw my hands in the air like I just dont care');
})();
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
interface User {
  name: string;
  age: number;
}
type Func = (user: User) => void;
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string

# Mapped Types

  • Mapped types build on the syntax for index signatures, which are used to declare the types of properties which have not been declared ahead of time
type p = {
  [key: number]: boolean;
};
const t: p = {
  1: true,
};
  • demo, take all the properties from the type Type and change their values to be a boolean
type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
};
type FeatureFlags = {
  darkMode: () => void;
  newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<FeatureFlags>;

/* turn as 
type FeatureOptions = {
    darkMode: boolean;
    newUserProfile: boolean;
}
*/
  • Modifiers
    • readonly with prefixing - or +
    • ?
type c<Type> = {
  /*
  type test = {
    id: string;
    name: string;
  }
  */
  -readonly [Property in keyof Type]: Type[Property];
  // if 
  readonly [Property in keyof Type]: Type[Property];
  /*
  type test = {
   readonly id: string;
   readonly name: string;
  }
  */
  // ?
  [Property in keyof Type]-?: Type[Property]; // reset match properties to NOT optional

};
type a = {
  readonly id: string;
  readonly name: string;
};
type test = c<a>;
  • Key Remapping
  • can re-map keys in mapped types, means the final keys's name have change
    • asMapped Types
type map<Type> = {
    [Properties in keyof Type as NewKeyType]: Type[Properties]
}
  • with condition
type ExtractPII<Type> = {
  [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};
 
type DBFields = {
  id: { format: "incrementing" };
  name: { type: string; pii: true };
};
type test = ExtractPII<DBFields>;
type test = {
    id: false;
    name: true; // input pii, so return true
}
 

# Template Literal Types

  • Mapped types which change properties via template literal strings
  • with as in Mapped Types to change properties (keys's name)
type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
    name: string;
    age: number;
    location: string;
}
type LazyPerson = Getters<Person>;
/*type LazyPerson = {
    getName: () => string; // key has change
    getAge: () => number;
    getLocation: () => string;
}
*/
  • Intrinsic, To help with string manipulation
    • Uppercase<StringType>
    • Lowercase<StringType>
    • Capitalize<StringType> Converts the first character in the string to an uppercase equivalent.
    • Uncapitalize<StringType> Converts the first character in the string to a lowercase equivalent.
Last Updated: 2021/11/17 下午2:27:50