Hello, 各位勇敢的小夥伴, 你們好, 我是大家的嘴強王者小五, 身體健康, 腦子沒病.html
本人有豐富的脫髮技巧, 能讓你一躍成爲資深大咖.前端
一看就會一寫就廢是本人的主旨, 菜到摳腳是本人的特色, 卑微中透着一絲絲剛強, 傻人有傻福是對我最大的安慰.react
歡迎來到
小五
的隨筆系列
之TypeScript 學習筆記
.typescript
本文初衷爲筆者在使用一段時間 TS 後, 對所學所想作一個記錄. 文章內容較爲基礎, 適合做爲入門級教程學習; 且涵蓋內容並不全面, 缺失包含類在內的諸多內容. 若是各位看官不介意以上幾點, 歡迎與筆者一同進入這嚴謹卻又妙不可言的奇幻旅途.數組
let flag: boolean = false;
let num: number = 10;
👺 擴展: number類型還支持 二進制(0b開頭)、 八進制(0o開頭)、 十六進制(0x開頭) 字面量. 運算或輸出時會轉換爲對應的十進制.app
let num02: number = 0b1010; let num08: number = 0o744; let num16: number = 0xf00d;
let str: string = '黃刀小五';
數組共有兩種定義方式: Array<T>
或 T[]
(T爲該數組每項的元素類型)函數
let numArr: number[] = [1, 2, 3]; let numArr: Array<number> = [1, 2, 3];
👺 特色: <數組>, <長度已知>, <每項元素類型不盡相同> post
😼 就是一個給每項都定義數據類型的數組學習
let tuple: [string, string, number, boolean?] = ['No.1', '黃刀小五', 18];
🦥 應用:ui
import { useState } from 'react'; const [loading, setLoading] = useState<boolean>(false); // 這裏類型可省略, 詳見後文類型推斷
type Touple = [string, number, string]; let csvData: Touple[] = [['黃刀小五', 18, '男'], ['二狗子', 14, '男']];
👺 擴展: 元組越界
let tuple: [number, string] = [1, '黃刀小五']; tuple.push(2); // ✅ right 元組越界時, 該項類型至關於對元組的各項類型作聯合 /* 過程以下, 看不懂的看官莫慌, 請先看後文 */ type UnionType<T> = T extends (infer P)[] ? P : never; type TupleType = UnionType<typeof tuple>; // number | string
enum Active { inactive, active, } enum Fruit { apple = 'apple', orange = 'orange', banana = 'banana', }
若枚舉類型未指定值或指定的值爲number類型, 如上述 Active, 可對其進行雙向取值: Active[0]
、Active['active']
, 其映射以下【👇】
{ 0: 'inactive', 1: 'active', inactive: 0, active: 1, }
可對枚舉的其中一項進行指定數值(一般爲第一項), 其他項會順序遞增
enum Type { active = 1, inactive, } { // 對應映射爲 1: 'active', 2: 'inactive', active: 1, inactive: 2, }
😼 tips: 建議採用賦值形式的枚舉. 可讀性更高, 如上述 Fruit
🦥 應用:
enum ActionType { ADD = 'ADD', EDIT = 'EDIT', DELETE = 'DELETE', } const reducer = (type: ActionType) => { switch(type) { case ActionType.ADD: // xxx break case ActionType.EDIT: // xxx break ... } } reducer(ActionType.ADD); // ✅ right let params = 'ADD'; reducer(params); // ❎ error (類型「string」的參數不能賦給類型「ActionType」的參數) /* 😼 tips: 非賦值形式你們可自行嘗試 */
enum Status { // 類型映射 unprocessed = 1, // 未處理 processed, // 已處理 refused, // 已拒絕 } enum Fruit { // 常量 apple = '蘋果', orange = '橘子', banana = '香蕉', }
👺 擴展: keyof typeof Enum
, 可將枚舉類型轉換爲聯合類型; 相信我, 你會用到的😏
enum ActionType { ADD, EDIT, DELETE, } type ActionTypeConst = keyof typeof ActionType // 'ADD' | 'EDIT' | 'DELETE'
let value: any;
😼 tips: 食物鏈最頂端, 應儘可能減小 any 的使用.
let value: unknown;
unknown 表示未知類型, 與 any 不一樣的是沒法對 unknown 類型執行任何操做. 仔細思考下 <any: 任意類型>、 <unknown: 未知類型> 的區別.
👉 以 number 的 toFixed()
方法舉例:
toFixed()
方法, 故不能使用;toFixed()
方法的我就是 number 類型;😼 tips: 如想使用 any, 請先考慮是否可用 unknown 代替. (搭配後文類型保護可安心食用)
經常使用於沒有具體返回值的函數
const fn = (str: string): void => { // 執行xxx事件 }
let u: undefined = undefined; let n: null = null;
這兩個不知道要說點啥, 就補充如下兩點吧 (然而與 TS 沒啥關係)
Number(undefined) => NaN
, Number(null) => 0
const fn = (arg?: string) => { ... }
arg的類型是 string | undefined
let n: never;
never | T = T
此特性可用來過濾掉不須要的值;🦥 應用:
type Type = string | number; const fn = (arg: Type) => { if (typeof arg === 'string') { ... } else if (typeof arg === 'number') { ... } else { const check: never = arg; } }
如上, 此時永遠不會走到 else, 下面咱們作如下操做:
- type Type = string | number; + type Type = string | number | boolean; * 此時 else 中的 check 會報錯 (不能將類型「boolean」分配給類型「never」)
若是沒有指定類型, TS 會根據類型推論推斷出一個類型.
let val; // 推論成: let val: any; let num = 10; // 推論成: let num: number = 10; num = '黃刀小五'; // ❎ error (不能將類型「string」分配給類型「number」)
🦥 應用: <單一靜態類型 - 如上述num>
、<函數返回值 - 通常狀況都可正確推斷其返回值類型, 不用額外指定>
、<循環中的子元素>
😼 tips: 若是 TS 能正確推斷出其類型, 咱們可採用類型推論而沒必要定義類型.
類型斷言用來告訴編譯器 「我知道本身在幹什麼」, 有 尖括號 和 as 兩種寫法. 在 $tsx$ 語法中, 只支持 as.
* 😼 tips: 下面咱們使用數組來舉個例子, 實際場景中應使用元組. type Key = string | number; let arr: Key[] = ['黃刀小五', 18]; // 不能將類型「Key」分配給類型「string」 - let name = arr[0]; - console.log(name.length); // 使用類型斷言 + let name = arr[0] as string; + let name = <string>arr[0]; + console.log(name.length);
接口用來定義對象的類型
interface User { readonly id: number; // 只讀屬性, 不可修改 name: string; // 必須屬性 desc?: string; // 可選屬性 say?: (name: string) => void; // 方法 } const say = (name: string) => { ... } let user: User = { id: 1, name: '黃刀小五', say, } user.id = 2; // ❎ error (沒法分配到 "id" ,由於它是隻讀屬性)
索引簽名 - 使接口更加靈活
interface User { [key: string]: string; // 表示 key 爲 string, value 爲 string 的任意屬性 } let user:User = { name: '黃刀小五', desc: '菜雞前端一隻', }
😼 tips: 全部成員都必須符合索引簽名的特徵, 索引簽名參數類型必須爲 string | number
interface User { [key: string]: string; age: number; // ❎ error (類型「number」的屬性「age」不能賦給字符串索引類型「string」。) }
接口合併
interface User { name: string; } interface User { age: number; } /* 二者會合併爲👇 */ interface User { name: string; age: number; }
接口繼承
關鍵字: extends
interface Person { name: string; } interface User { age: number; } interface Student extends Person, User { desc: string; } /* Student接口格式以下👇 */ interface Student { name: string; age: number; desc: string; }
😼 tips: 接口和類型別名都可使用的狀況下使用接口
🤔 思考: ❓ 如何定義一個樹形結構
interface Tree { key: number; value: string; child?: Tree[]; } let tree: Tree[] = [];
顧名思義, 爲該類型取一個新的名字
type Key = string | number;
與 Interface 對比
type Key = string | number; // 表明 string 或 number 類型
😼 tips: 聯合類型能夠用來聲明具體的值
type Status = 'active' | 'inactive';
interface User { name: string; } interface Student { age: number; } type Blogger = User & Student; /* Blogger類型格式以下👇 */ { name: string; age: number; }
😼 tips: 兩個基礎類型作交叉, 會生成 never 類型
type Key = string & number; // never, 沒有類型能夠知足便是 string 又是 number
類型查找能夠提取對象類型上某一屬性的類型
interface Person { User: { name: string; age?: number; } } type User = Person['User']
😼 tips: 經常使用於第三方庫類型沒法引用的場合
👺 配合類型斷言 as 來聲明常量
type Status = 'active' | 'inactive'; const fn = (status: Status) => { ... } - let status = 'active'; // 此時 'active' 被解析爲字符串而很是量 - fn(status); // ❎ error (類型「string」的參數不能賦給類型「Status」的參數) * 如下3種等價, 都可將 'active' 解析爲常量 + let status = 'active' as const; + const status = 'active'; + let status: Status = 'active'; + fn(status); // ✅ right
😼 tips: 第三方插件定義的常量常常須要配合 as const
使用呦
👺 用來獲取變量的類型
let str = '黃刀小五'; type Str = typeof str; // type Str = string let user = { name: '黃刀小五' } type User = typeof user; // type User = { name: string; }
👺 用來提取對象類型的 key 值
interface User { name: string; age?: number; } type Key = keyof User; // type Key = 'name' | 'age'
❓ 爲何須要類型保護
const fn = (value: string | number) => { if (value.length) { ... } // ❎ error (類型「string | number」上不存在屬性「length」) }
上述代碼, 若是想在 value: string
時執行一段邏輯要怎麼辦呢?
此時就須要類型保護了, 使用類型保護後, 當前代碼段會按照事先所指定的類型執行.
👺 typeof
const fn = (value: string | number) => { if (typeof value === 'string') { // 利用 typeof 限制 value 的類型爲 string console.log(value.length); } }
👺 is
const isString = (x: unknown): x is string => { return typeof x === 'string'; } const fn = (value: string | number) => { if (isString(value)) console.log(value.length); // value is string }
👺 in
表示某屬性是否在當前對象中存在
interface User { name: string; age?: number; } const fn = (args: User) => { if ('age' in args) { ... } else { ... } } fn({ name: '黃刀小五', age: 18 }); // 執行 if 語句 fn({ name: '黃刀小五' }); // 執行 else 語句
👺 做用: 用於作代碼複用
先來看個例子
const fn = (value: number): number => value;
🤔 思考 ❓ 若是此時我想傳入一個 string 類型並返回一個 string 類型呢
const fn = <T>(value: T): T => value; fn<string>('黃刀小五'); // const fn: <string>(value: string) => string * 😼 tips: 可根據 類型推斷 推斷出其爲 string 類型, 而不用特地指定 -> `fn('黃刀小五');`
以上, 一個泛型就實現好了, 其能夠將類型做爲一個參數, 在調用時傳入類型進行指定.
👺 注意: 在 $tsx$ 中, <T>
會被解析成標籤, 可以使用下面的寫法:
const fn = <T extends {}>(value: T): T => value; // extends也可用於縮小T的範圍 const fn = <T,>(value: T): T => value;
const fn = <T, U>(type: T, value: U): U => { ... }; fn<boolean, string>(true, '黃刀小五');
泛型的做用至關之廣, 如定義一個接口:
interface Teacher<T> { readonly id: number; name: string; student?: T[]; } interface Student { readonly id: number; name: string; age?: number; } let teahcer: Teacher<Student> = { id: 1, name: '黃刀小五', student: [{ id: 1001, name: '二狗子', age: 14, }] }
下面咱們來用上述知識實現下 TS 中封裝好的語法糖 (舒適提示: 建議先搞懂上文, 至少搞懂泛型在閱讀下面內容)
look down 👇
👺 將對象類型的屬性均變爲只讀
type Readonly<T> = { readonly [K in keyof T]: T[K]; };
interface Person { readonly name: string; age: number; desc?: string; } let person: Readonly<Person> = { name: '黃刀小五', age: 18, } person.age = 19; // ❎ error (沒法分配到 "age" ,由於它是隻讀屬性)
👺 將對象類型的屬性均變爲可選
type Partial<T> = { [K in keyof T]?: T[K]; };
interface Person { readonly name: string; age: number; desc?: string; } let person: Partial<Person> = {}; // 此時全部屬性均爲可選
👺 將對象類型的屬性均變爲必須
type Required<T> = { [K in keyof T]-?: T[K]; };
😼 tips: -? 表示去掉可選符號 ?, 此符號 (-) 一樣可用於其它位置, 如: -readonly 可去掉只讀屬性等.
interface Person { readonly name: string; age: number; desc?: string; } let person: Required<Person>> = { name: '黃刀小五', age: 18, desc: '基於搜索引擎的複製粘貼工程獅', }; // 此時全部屬性均爲必須
👺 將一個類型的全部屬性值映射到另外一個類型上並建立一個新的類型
type Record<K extends string, T> = { [P in K]: T; };
interface Person { readonly name: string; age: number; desc?: string; } type Kind = 'teacher' | 'student'; let person: Record<Kind, Person> = { teacher: { name: '黃刀小五', age: 18, }, student: { name: '二狗子', age: 14, } }; // 將 Person 類型映射到 Kind 類型中 /* Record<Kind, Person> 至關於👇 */ type NewPerson = { [key in Kind]: Person };
👺 從 T 中提取 U
type Extract<T, U> = T extends U ? T : never;
type Student = '二狗子' | '如花'; type Teacher = '黃刀小五' | '二狗子'; type Trainee = Extract<Student, Teacher>; // 輸出 '二狗子' /* Extract<Student, Teacher> 至關於👇 */ '二狗子' in Teacher -> '二狗子' '如花' in Teacher -> never '二狗子' | never = '二狗子'
👺 從 T 中排除 U
type Exclude<T, U> = T extends U ? never : T;
type Student = '二狗子' | '如花'; type Teacher = '黃刀小五' | '二狗子'; type OnlyStudent = Exclude<Student, Teacher>; // 輸出 '如花' /* Exclude<Student, Teacher> 至關於👇 */ '二狗子' in Teacher -> never '如花' in Teacher -> '如花' '如花' | never = '如花'
👺 挑選對象中的部分屬性
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
interface Person { readonly name: string; age: number; desc?: string; } let person: Pick<Person, 'age' | 'desc'> = { age: 18, }; /* Pick<Person, 'age' | 'desc'> 至關於👇 */ interface Person { age: number; desc?: string; }
👺 忽略對象中的部分屬性
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
interface Person { readonly name: string; age: number; desc?: string; } let person: Omit<Person, 'name'> = { age: 18, }; /* Omit<Person, 'name'>至關於👇 */ interface Person { age: number; desc?: string; }
👺 獲取方法的返回值類型
type ReturnType<T> = T extends ( ...args: any[] ) => infer R ? R : any;
-> infer 配合 extends 使用, 用於推斷函數的返回值類型
const getName = (name: string) => name; type ReturnGetName = ReturnType<typeof getName>; // string
👺 獲取方法的參數類型
type Parameters<T> = T extends ( ...args: infer P ) => any ? P : never;
-> 這裏 infer 用於推斷函數的參數類型, Parameters 返回格式爲元組.
const getName = (name: string) => name; type ParamGetName = Parameters<typeof getName>; // [name: string]
👺 排除 null 和 undefined 類型
type NonNullable<T> = T extends null | undefined ? never : T;
type NewPerson = Person | null; let person: NonNullable<NewPerson> = null; // ❎ error (不能將類型「null」分配給類型「Person」)
結合文章內容, 你們可根據實際需求封裝更多的語法糖, 便於在項目中使用. 若想更細緻的學習 TS, 這裏推薦一個博主 阿寶哥, 其發表了不少關於 TS 的文章, 並針對 TS 的某一特徵作了詳細的講解.