TypeScript三部曲之基礎篇

前言

TypeScriptJavaScript的超集,爲JavaScript賦予了強類型語言的特性。在項目中使用TypeScript可以將錯誤提早暴露在開發階段,而且TypeScript除保留JavaScript原有特性外還增長了不少強大的特性,如:接口泛型等等,爲開發帶來了極大的便利。前陣子在學習TypeScript過程當中整理了很多筆記,在梳理過程當中就產生了該文章,同時也但願讓更多同窗瞭解TypeScript,順便幫本身加深記憶。node

TypeScrip中的數據類型

TypeScript的數據類型,包含了JavaScript全部的數據類型。除此以外還提供了枚舉元組anyvoid等類型,下面講述在TypeScript中如何使用這些類型。typescript

JavaScript中原有的數據類型

  • Boolean
// Boolean
let bool: boolean = true;
複製代碼
  • String
// String
let str: string = 'hello TypeScript';
複製代碼
  • Number
    • JavaScript同樣,TypeScript裏的全部數字都是浮點數
    • 除支持十進制、十六進制外,TypeScript還支持二進制、八進制
// Number
let decLiteral: number = 10;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
複製代碼
  • Array
    • Array分兩種聲明方式:
      • 第一種,能夠在元素類型後面接上 [],表示由此類型元素組成的一個數組
      • 第二種方式是使用數組泛型,Array<元素類型>
// 第一種,能夠在元素類型後面接上 [],表示由此類型元素組成的一個數組
let arr1: number[] = [1, 2, 3];

// 第二種方式是使用數組泛型,Array<元素類型>
let arr2: Array<number> = [1, 2, 3];

// 數組支持多種元素類型
let arr3: (number | string) = [1, '2', 3];
let arr4: Array<number | string> = [1, '2', 3];
複製代碼
  • Function
// Function
let add = (x, y) => x + y;
複製代碼
  • Object
// 能夠這樣定義,但訪問、修改屬性時會拋出錯誤,由於Object上不存在x、y屬性。
let obj: Object = {x: 1, y: 2}; 
console.log(obj.x); // 類型「Object」上不存在屬性「x」

// 完善定義
let obj1: {x: number, y: number} = {x: 1, y: 2};
console.log(obj1.x); // 1
複製代碼
  • Symbol
// Symbol
let syb: symbol = Symbol('hello');
複製代碼
  • null與undefined
// null與undefined
let null: null = null;
let undefined: undefined = undefined;
複製代碼

上面示例中咱們在變量後跟類型的方式,被稱之爲:類型註解。使用類型註解的變量必須按照類型註解聲明的類型來賦值,不然TypeScript會拋出錯誤。編程

TypeScript新增的數據類型

  • void
    • void表明沒有返回值,或返回值爲undefined,一般用來聲明函數的返回值類型。
    • 若是一個變量被申明爲void類型,那它只能被賦值爲nullundefeated
// void
let add = (x: number, y: number): void => undefined; // 表示返回值爲undefined
let unusable: void = undefined;
unusable = 'hello'; // ts會拋出錯誤,不能將類型「"hello"」分配給類型「void」
複製代碼
  • any
    • any能夠被賦值爲除never外的任何類型。
    • 注:可使用any,可是不建議所有使用any來定義類型,這樣使用TypeScript就沒有任何意義了。
// any
let any: any = 1;
any = '1';
any = null;
複製代碼
  • never
    • never類型表示的是那些永不存在的值的類型
    • 返回never的函數必須存在沒法達到的終點,好比:一個死循環的函數、一個老是拋出錯的函數。
    • never只能被賦值爲never自身。
// never
function loop(): never {
  while(true) {}
}

function error(message: string): never {
  throw new Error(message);
}
複製代碼
  • 元組
    • 元組類型表示一個已知元素數量和類型的數組,各元素的類型沒必要相同
    • 元組越界會拋出錯誤
// 元祖 tuple

let tuple: [number, string] = [1, '2'];
tuple[2] = 2; // 不能將類型「2」分配給類型「undefined」。
複製代碼
  • 枚舉
    • 枚舉分爲兩種類型:數字枚舉字符串枚舉
    • 枚舉成員的值爲只讀類型,不能修改
    • 默認爲數字枚舉,從0開始,依次遞增
    • 枚舉成員類型分爲兩類:常量枚舉計算型枚舉
      • 常量枚舉:常量枚舉會在編譯時計算出結果,已常量的方式保存。常量枚舉賦值分如下三種狀況:
        1. 沒有初始值
        2. 對已有成員的引用
        3. 常量表達式
      • 計算型枚舉:須要被計算的枚舉成員,計算型枚舉不會在編譯時計算,會保留到執行階段。
        1. 計算型枚舉後的枚舉成員必需要賦值
    • 須要注意的是,包含字符串的枚舉中不能使用計算型枚舉
    • 注:一般枚舉會用於權限、狀態判斷。避免"魔術數字"的出現,提升可讀性、可維護性。
// 數字枚舉,默認從0開始,依次遞增
enum Color {
  Red,
  Green,
  Blue
}

console.log(Color.Red) // 0
console.log(Color.Red) // 1
console.log(Color.Red) // 2

// 自定義數字枚舉的值
enum Alphabet {
  A = 8,
  B,
  C = 2020
}

console.log(Alphabet.A) // 8
console.log(Alphabet.B) // 9
console.log(Alphabet.C) // 2020

// 類型保護
let year = Alphabet.C; 
year = 'today'; // 不能將類型「"today"」分配給類型「Alphabet」

// 數字枚舉支持雙向映射
console.log(Alphabet.A) // 8
console.log(Alphabet[8]) // A

// js實現雙向映射
var Alphabet;
(function (Alphabet) {
    Alphabet[Alphabet["A"] = 8] = "A";
    Alphabet[Alphabet["B"] = 9] = "B";
    Alphabet[Alphabet["C"] = 2020] = "C";
})(Alphabet || (Alphabet = {}));

// 字符串枚舉
enum Message {
  Fail = '失敗',
  Success = '成功'
}

// 枚舉成員類型
enum Library {
  // 常量枚舉
  BookName,
  Year = 2020,
  Count = BookName + Year,
  // 計算型枚舉
  Number = Math.random(),
  Size = '如何成爲掘金V6'.length // 計算型枚舉後的枚舉成員必需要賦值
}
複製代碼

看完上面的內容,咱們已經對TypeScript中的數據類型已經有一個大概的瞭解了。有同窗可能會有疑問,若是咱們須要描述一個複雜的數據類型該怎麼編寫類型註解呢?這就涉及到咱們下面講的內容接口,如何使用接口來描述一個複雜的數據類型。數組

接口

接口TypeScript中核心概念之一,主要用於約束對象、函數、類的結構dom

  • 如何聲明一個接口?
    • 可以使用interface關鍵字聲明接口
  • 接口能夠限制哪些屬性?
    • 可選屬性:x?: string,來約定參數是否可選
    • 只讀屬性:readonly x: number ,來約定參數是否只讀
  • 接口分幾種類型?
    • 對象類型
    • 函數類型
      • 函數的參數名不須要與接口裏定義的名字相匹配,但要求對應位置上的類型是兼容的
    • 索引類型
    • 混合類型
  • 注:接口具備鴨式辯型法特性。
    • 什麼是鴨式辯型法
      • 答:一隻鳥若是走起來像鴨子,叫起來像鴨子,遊起來像鴨子,那麼這隻鳥就能被當成一隻鴨子。這就是鴨式辨型法,下面咱們會舉例說明。

接口的幾種類型

/** * 對象類型接口 */

// 可選屬性與只讀屬性
interface Person {
  name: string;
  readonly age: number;
  sex: 'boy' | 'girl',
  hobby?: string[]
}

let Tom: Person = {
  name: 'Tom',
  age: 3,
  sex: 'boy'
};
Tom.age = 1; // Cannot assign to 'age' because it is a read-only property.

let Jerry: Person = {
  name: 'Jerry',
  age: 3,
  sex: 'boy',
  hobby: ['和湯姆一塊兒快樂的玩耍']
}

/** * 鴨式辯型法舉例 * renderList傳入的參數只要知足Result接口的約定便可,多餘的code、msg不會影響 * 這也是咱們提到的:一隻鳥若是走起來像鴨子,叫起來像鴨子,遊起來像鴨子,那麼這隻鳥就能被當成一隻鴨子。 */
interface ListItem {
  id: number;
  name: string
}

interface Result {
  list: ListItem[]
}

function renderList(result: Result) {
  result.list.forEach((item: ListItem) => {
    console.log(item.id, item.name)
  })
}

const data = {
  list: [{
    id: 1,
    name: '小紅'
  }, {
    id: 2,
    name: '小白'
  }],
  code: 1,
  msg: ''
};
renderList(data);

/** * 函數類型接口 * 函數的參數名不須要與接口裏定義的名字相匹配,但要求對應位置上的類型是兼容的 */
let add: (x: number, y: number) => number;

interface Add {
    (x: number, y: number): number;
}
// 上面這兩種定義式等價的

// 函數的參數名不須要與接口裏定義的名字相匹配
let add1: Add = (j: number, k: number) => j + k;

// 但要求對應位置上的類型是兼容的
let add2: Add = (j: string, k: number) => j + k; // 不能將類型「(j: string, k: number) => string」分配給類型「Add」。參數「j」和「x」 的類型不兼容。

/** * 索引類型接口 * 索引類型具備一個 索引簽名,它描述了對象索引的類型,還有相應的索引返回值類型。 * 當不肯定接口中屬性個數時可使用索引類型接口 * 索引類型分字符串索引和數字索引 * 字符串與數字索引能夠同時使用,但數字索引必須是字符串索引返回的子類型 */

// 數字索引
interface StringArray {
  [index: number]: string;
}
const myArra1: StringArray = ['1', 2]; // 不能將類型「number」分配給類型「string」。
const myArray: StringArray = ['1', '2'];

// 字符串索引
interface ListItem {
  id: number;
  name: string;
  [x: string]: any;
}

// 這裏咱們使用string去索引any
let list: ListItem[] = [{
  id: 1,
  name: 'aa',
  year: '2019'  
}]

// 字符串與數字索引能夠同時使用,但數字索引必須是字符串索引返回的子類型
interface ArrayItem {
  [x: string]: string;
  [y: number]: number; // 數字索引類型「number」不能賦給字符串索引類型「string」
}

interface ArrayItem1 {
  [x: string]: string;
  [y: number]: string;
}

/** * 混合類型接口 * 混合類型接口是函數類型接口與對象類型接口的集合 */
interface Lib {
  (): void;
  version: string;
  doSomething(): void;
}

let lib: Lib = (() => {}) as Lib; lib.version = '1.0.0'; lib.doSomething = () => {}; 
複製代碼

類型別名與類型斷言

  • 什麼是類型別名?
    • 一般咱們會使用interface聲明接口,但類型別名也能達到一樣的做用,甚至更方便簡潔。
      • 如何聲明類型別名?
        • 類型別名使用type關鍵字聲明。
  • 什麼是類型斷言?
    • 有時候你會比TypeScript更清楚數據的類型,這時候就能使用類型斷言,表示你清楚該數據的類型格式。
      • 如何使用類型斷言
        1. 與JSX一致,使用<類型>的方式來聲明類型斷言。
        2. 使用as進行類型斷言。
        3. 注:第一種方式在React中會與JSX衝突,推薦統一使用第二種方式。
// 使用接口聲明一個函數類型接口
interface Add {
  (x: number, y: number): number
}

// 使用類型別名聲明一個函數類型接口
type Add = (x: number, y: number) => number;

// 上面兩種聲明方式等價

/** * 類型別名的一些小技巧 * 在React中初始化state,設置類型比較麻煩 * 若是使用類型別名就很方便了 */

const initialState = {
  page: 1,
  pageCount: 15,
  list: [{
    id: 1,
    name: 'Tom'
  }]
};

type State = typeof initialState;

type State1 = {
    page: number;
    pageCount: number;
    list: {
        id: number;
        name: string;
    }[];
}
// State與State1二者等價

/** * 類型斷言 * 類型斷言有兩種聲明方式:1. 與JSX通常聲明,2. 使用as關鍵字聲明 * 在React中請使用第二種方式,同時也推薦使用第二種方式 */

interface Obj {
  x: number;
  y: number;
}
let obj = {};
obj.x = 1; // 類型「{}」上不存在屬性「x」

// 1. 如JSX通常聲明
let obj1 = <Obj>{};
obj1.x = 1;

// 2. 使用as關鍵字聲明
let obj2 = {} as Obj;
obj2.x = 1;
複製代碼

函數

TypeScript中的函數行爲基本與JavaScript保持一致,不一樣的是TypeScript支持如下功能:函數

  1. 支持約束參數類型、數量
    • 函數必須按照約定參數類型與數量傳入參數,不然會拋出錯誤。
  2. 支持定義函數返回值的類型
    • 表示函數是否存在返回值、以及返回值的類型
  3. 支持可選參數
    • 可選參數必須位於必選參數以後
  4. 支持函數重載
    • TypeScript規定必須在類型最寬泛的版本中實現重載,你能夠理解爲在類型最爲寬泛的版本中須要支持以前版本的全部類型。
// 約束參數類型、數量
function add(x: number, y: number) {
  return x + y;
};
add(1, 2); // 3
add(1, '2'); // 類型「"2"」的參數不能賦給類型「number」的參數
add(1, 2, 3); // 應有 2 個參數,但得到 3 個。

/** * 可選參數與聯合類型 * 可選參數除參數約定類型外,還會默認支持undefined * 可選參數都是聯合類型 * 聯合類型是指使用「|」設置支持多種類型,後續內容會詳細講解 */
function add1(x: number, y?: number) {
  return y ? x + y : x;
}
add1(1); // 1
add1(1, 2); // 3

// 默認參數
function add2(x: number, y: number = 2020) {}

// 剩餘參數
function add3(x: number, ...rest: number[]){
  return rest.reduce((pre: number, cur: number) => pre + cur, x);
}
add2(1, 2, 3, 4, 5, 6); // 21

/** * 函數重載 * TypeScript規定必須在類型最寬泛的版本中實現重載,你能夠理解爲在類型最爲寬泛的版本中須要支持以前版本的全部類型。 */
function add4(...rest: number[]): number;
function add4(...rest: string[]): string;
function add4(...rest: any) {
  let first = rest[0]
  if (typeof first === 'string') {
    return rest.join(' ')
  }
  if (typeof first === 'number') {
    return rest.reduce((pre: number, cur: number) => pre + cur)
  }
}

add4(2020, 1, 17) // 2038
add4('hello','TypeScript') // hello TypeScript
add4({x: 1, y: 2}) // 類型「{ x: number; y: number; }」的參數不能賦給類型「string」的參數
複製代碼

JavaScript自己是沒有類概念,在ECMAScript 6以前都是使用函數與基於原型的繼承建立可複用組件。 JavaScript在ECMAScript 6引入了class關鍵字,可用於基於類的面向對象編程。可是class的本質仍是函數+原型,此處很少作講解。TypeScript的類包含JavaScript的類,除此以外還新增類一些新特性如:成員修飾符抽象類多態等等。oop

繼承與成員修飾符

  • TypeScript有哪些成員修飾符以及特性?
    • public,公共成員
      • 特性:公共成員都能訪問
    • prviate,私有成員
      • 特性:私有成員不能被外部調用
    • protected,受保護成員
      • 特性:只能在類及其子類中訪問。
    • readonly
      • 特性:只讀屬性,不可被更改
    • static
      • 特性:只能經過類名訪問,不能經過子類訪問
  • TypeScript類的屬性必須具備初始值。
  • TypeScript類屬性默認爲public,公共成員
/** * 繼承與成員修飾符 */

interface PersonBase {
  name: string;
  age: number;
  sex: 'boy' | 'girl';
}

class Person {
  name: string;
  age: number;
  readonly sex: string; // 只讀屬性,不可被更改
  constructor({ name, age, sex }: PersonBase) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
}

class Man extends Person {
  private wife: string; // 私有成員不能被外部調用
  protected propertyList?: string[]; // 受保護成員,只能在類及其子類中訪問。
  static weight: number = 120; // 只能經過類名訪問,不能經過子類訪問
  constructor(personBase: PersonBase) {
    super(personBase);
    this.wife = 'Jilly';
    this.propertyList = ['house', 'car'];
  }

  private getWife() {
    return this.wife;
  }

  getWeight() {
    return this.weight; // Property 'weight' is a static member of type 'Man'
  }
}

class Son extends Man {
  constructor(personBase: PersonBase) {
    super(personBase);
  }
}

const Tom = new Man({
  name: 'Tom',
  age: 18,
  sex: 'boy'
});
const Child = new Son({
  name: 'child',
  age: 3,
  sex: 'boy'
});
Child.sex = 'girl'; // Cannot assign to 'sex' because it is a read-only property.
console.log(Tom.propertyList); // 屬性「propertyList」受保護,只能在類「Man」及其子類中訪問。
console.log(Child.getWife()); // 屬性「getWife」爲私有屬性,只能在類「Man」中訪問。
console.log(Man.weight); // 只能經過類名訪問,不能經過子類訪問
複製代碼

抽象類與多態

  • 什麼是抽象類?
    • 抽象類一般做爲基類,將多個事物中的共性抽離,方便其餘派生類繼承。
  • 抽象類有什麼特色?
    • 只能被繼承,不能被實例化。
  • 如何建立抽象類?
    • 抽象類使用abstract關鍵字定義抽象類與抽象方法。
  • 什麼是多態?
    • 在父類中定義,在子類中能夠有不一樣的實現,此爲多態。
/** * 抽象類與多態 * 抽象類只能被繼承,不能被實例化。 * 抽象類中的抽象方法不包含具體實現而且必須在派生類中實現 */
 
// 實現一個抽象類
abstract class Animal {
  eat() {
    console.log('eat');
  }
  // 實現一個抽象方法,抽象方法不包含具體實現而且必須在派生類中實現
  abstract sleep(): void;
}

const pig = new Animal(); // 沒法建立抽象類的實例

class Dog extends Animal {
  name: string;
  constructor(name: string){
    super();
    this.name = name;
  }

  sleep() {
    console.log('dog sleep')
  }
}

let dog = new Dog('wang wang');
dog.sleep()

// 非抽象類「Cat」不會實現繼承自「Animal」類的抽象成員「sleep」
class Cat extends Animal {
}
複製代碼

類與接口

  • 接口與接口之間可相互繼承,使用extends關鍵字繼承。
  • 繼承多個接口須要用「,」分割
  • 類使用接口時,須要使用implements關鍵字
  • 接口與類可相互繼承
/** * 類與接口 * 接口與接口之間可相互繼承,使用extends關鍵字繼承。 * 繼承多個接口須要用「,」分割 * 類使用接口時,須要使用implements關鍵字 * 接口與類可相互繼承 */

interface Animal {
  eat(): void;
}

// 接口繼承接口
interface Felidae extends Animal {
  run(): void;
}

interface Pets {
  cute: boolean;
}

// 同時繼承多個接口
interface Cat extends Felidae, Pets {}

// 類使用接口
class Tom implements Cat {
  cute = true;
  eat(){};
  run(){};
}

// 接口繼承類
interface Jack extends Tom {}
複製代碼

泛型

  • 什麼是泛型?
    • 不預先確認數據格式,具體的類型格式在使用時才能確認。
  • 泛型的優化:
    1. 函數和類能夠輕鬆支持多重屬性,加強程序的可擴展性
    2. 沒必要寫多條函數重載,冗長的聯合類型聲明,加強代碼可讀性。
    3. 靈活控制類型之間的約束。
  • 注:泛型不能用於類的靜態成員
/** * 泛型 * 不預先確認數據格式,具體的類型格式在使用時才能確認。 * 泛型不能用於類的靜態成員 */

// 一個簡單的泛型函數
function log<T>(msg: T): T {
  return msg;
}

// 實現一個泛型接口
interface Log {
  <T>(msg: T): T
}

// 上面兩種實現方式等價

// 兩種使用方式
log<string>('hello'); // 第一種,告訴泛型函數傳入的參數是什麼類型
log('hello'); // 第二種,經過TypeScript類型推斷來肯定類型

let myLog: Log = <T>(msg: T) => msg;
myLog<string>('hello');
myLog('hello');

class Cat<T> {
  static eat(name: T) {} // 靜態成員不能引用類類型參數
}

複製代碼

類型保護機制

類型保護機制,可以保證TypeScript變量在特定的區塊內屬於特定的類型,所以,在類型保護區塊中,能夠放心的引用對應類型的屬性和方法。學習

/** * 類型保護機制 * 類型保護機制能肯定在指定區塊內,可使用對應類型的方法和屬性 * 可以使用instanceof、typeof、in、hasOwnProperty或組合判斷等方式來肯定區塊 */
class Pig {
  edible: boolean = true;
}

class Cat {
  cute: boolean = true;
}

function getAnimal(animal: Pig | Cat) {
  const Animal = animal instanceof Pig ? new Pig() : new Cat();
  if (Animal instanceof Pig) {
    console.log(Animal.edible); // true
  } else {
    console.log(Animal.cute); // true
  }
}
複製代碼

高級類型

交叉類型與聯合類型

  • 什麼是交叉類型
    • 將多個類型合併爲一個類型,能夠認爲是多個類型的集合。
  • 如何實現交叉類型
    • 使用&將多個類型合併爲一個類型。
  • 什麼是聯合類型
    • 將變量設置多種類型,賦值時能夠根據設置的類型來賦值。
  • 如何實現聯合類型
    • 使用|來設置多個類型, 賦值時能夠根據設置的類型來賦值。
/** * 交叉類型與聯合類型 * 交叉類型會將多個類型合併爲一個類型,能夠認爲是多個類型的集合。 * 交叉類型使用&將多個類型合併爲一個類型。 * 聯合類型會將變量設置多種類型,賦值時能夠根據設置的類型來賦值。 * 聯合類型使用|來設置多個類型, 賦值時能夠根據設置的類型來賦值。 */

interface Dog {
  run: () => void;
}

interface Cat {
  jump: () => void;
}

// 交叉類型
let pet: Dog & Cat = {
  run: () => {},
  jump: () => {}
};

// 聯合類型
let Sex: 'boy' | 'girl';
let Status: 'success' | 'fail' | 'loading'
複製代碼

索引類型

  • 索引類型讓靜態檢查可以覆蓋到類型不肯定(沒法窮舉)的」動態「場景。 例如,一個常見的JavaScript模式是從對象中選取屬性的子集。
  • keyof T索引類型查詢操做符,T你能夠理解爲任何類型與值。
  • T[K]索引訪問操做符,與獲取對象類似,存在返回值的類型,不存在會拋出錯誤。
/** * 索引類型 */

let obj = {
  a: 1,
  b: 2,
  c: 3
};

// keyof T
interface Obj {
  a: number;
  b: string;
}
let key: keyof Obj; // "a" | "b"

// T[K]
let value: Obj['a']; // number

/** * T extends U * 這個函數的意思是,從K[]中找到T包含的索引,返回類型爲T[K] * 簡單的來講就是,從keys中找到obj中包含的值,keys若是傳入obj中不存在的值就會拋出錯誤 */

function getValue<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
  return keys.map(key => obj[key]);
}
console.log(getValue(obj, ['a', 'b'])); // [1, 2]
console.log(getValue(obj, ['a', 'f'])); // 不能將類型「string」分配給類型「"a" | "b" | "c"」 [1, undefined]
複製代碼

映射類型

  • 什麼是映射類型
    • 從舊類型中建立新類型的一種方式。新的類型以相同的形式去轉換舊的類型,好比能夠令每一個屬性成爲readonly或者可選的。
/** * 映射類型 * 從舊類型中建立新類型的一種方式。新的類型以相同的形式去轉換舊的類型,好比能夠令每一個屬性成爲readonly或者可選的。 * TypeScript自身提供了不少種映射類型,如:Readonly、Partial、Pick、Record等等 * 有興趣的同窗能夠查看 node_modules/typescript/lib/lib.es5.d.ts 文件 */

interface Obj {
  a: number;
  b: string;
  c: boolean;
}

// 只讀
type ReadonlyObj = Readonly<Obj>

// 可選
type PartialObj = Partial<Obj>

// 抽離指定屬性
type PickObj = Pick<Obj, 'a' | 'b'>

type RecordObj = Record<'x' | 'y', Obj>
複製代碼

條件類型

  • 什麼是條件類型
    • 顧名思義就是根據條件來返回類型。
  • TypeScript 2.8在lib.d.ts裏增長了一些預約義的有條件類型:
    • Exclude<T, U> -- 從T中剔除能夠賦值給U的類型。
    • Extract<T, U> -- 提取T中能夠賦值給U的類型。
    • NonNullable<T> -- 從T中剔除nullundefined
    • ReturnType<T> -- 獲取函數返回值類型。
    • InstanceType<T> -- 獲取構造函數類型的實例類型。
/** * 條件類型 */
// T extends U ? X : Y;

type TypeName<T> = 
  T extends string ? "string" :
  T extends number ? 'number' :
  T extends boolean ? 'boolean' :
  T extends undefined ? 'undefined' :
  T extends Function ? 'function' :
  'object';

type T1 = TypeName<number>; // number
type T2 = TypeName<string[]> // object

// (A | B) extends U ? X : Y;
type T3 = TypeName<number | string[]>; // "number" | "object"

// Diff的意思是從T中找出U中不存在的值,存在返回never,不存在返回該值
type Diff<T, U> = T extends U ? never : T;
type T4 = Diff<'a' | 'b' | 'c', 'a' | 'e'>
// Diff <'a', 'a', 'e'> | Diff <'b', 'a', 'e'> | Diff <'c', 'a', 'e'>
// never | 'b' | 'c'
// 'b' | 'c'

type NotNull<T> = Diff<T, undefined | null>;
type T5 = NotNull<string | number | undefined | null> // string | number

// Exclude<T, U>
// NotNullable<T>
// Extract<T, U>
type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>; // a

// RenturnType<T>
type T7 = ReturnType<() => string> // string
複製代碼

結語

到此TypeScript三步曲之基礎篇就結束了,感謝你們的閱讀,若有錯誤歡迎斧正。優化

同時2020年的第一個flag也實現了,在2020年1月20日回家前輸出該文章,雖然花了不少時間和精力,但也讓我更深刻的瞭解了TypeScript而且加深了印象。後續的TypeScript三步曲之配置篇TypeScript三步曲之工程篇預計3-4月輸出。ui

明天就回家過年了,在此提早祝各位掘友:新年快樂、身體健康、事事如意。

相關文章
相關標籤/搜索