TypeScript 入門與實戰(zhàn)
-
tsconfig.json 編譯配置文件 compilerOptions
- 啟用
--strict
編譯選項刑枝,開啟嚴格模式 - 啟用
--strictNullChecks
抄囚,undefined 值只能夠賦值給 undefined 類型菠发,null 值只能夠賦值給 null 類型封拧,實際上這種表述不完全準確芜茵。因為在該模式下夷都,undefined 值和 null 值允許賦值給頂端類型,同時 undefined 值也允許賦值給 void 類型尤莺。 - 啟用
--noImplicitAny
旅敷,禁用隱式 any 類型轉(zhuǎn)換 - 啟用
--noImplicitThis
,this 不能隨意.任意值 - 啟用
--strictPropertyInitialization
颤霎,類的成員變量初始化檢查媳谁,需要與 --strictNullChecks 同時使用 - 啟用
--strictFunctionTypes
,嚴格函數(shù)類型檢查友酱,false 時 對應(yīng)位置上的參數(shù)只要存在子類型關(guān)系即可晴音,而不強調(diào)誰是誰的子類型 - 啟用
--noStrictGenericChecks
,非嚴格泛型函數(shù)類型檢查缔杉,即忽略函數(shù)的參數(shù)類型 -
--importsNotUsedAsValues: remove|preserve|error
锤躁,如何處理 import type,刪除/保留/強制 import type -
moduleResolution: Classic|Node
或详,默認與--module
相關(guān):--module CommonJS --moduleResolution Node
,--module ES6 --moduleResolution Classic
- 啟用
//#region //#endregion 定義了代碼折疊區(qū)域的起止位置
-
es6 規(guī)范中定義的 7 種數(shù)據(jù)類型:Undefined系羞、Null、Boolean鸭叙、String觉啊、Symbol、Number沈贝、Object
- unique symbol:ts 中只允許用 const 或 readonly 來定義
- unique symbol:ts 中只允許用 Symbol()或 Symbol.for()初始化(“確保”其唯一性)
- unique symbol:ts 中將 Symbol.for('same')賦值給兩個變量勋乾,編譯器會認為它們是兩個不同的值宋下,即使他們相同。
-
枚舉:無初始值辑莫,從前一個枚舉成員的值+1(如果是首個為 0)
- 變量:數(shù)值型枚舉 = number ??学歧; 變量:字符串枚舉 = string ?
- 在帶有字符串成員的枚舉中不允許使用計算值
- 枚舉成員映射(僅數(shù)值型枚舉):Bool[Bool.False] // 'False'
-
const enum
編譯后會被替換成字面量,非const
會被轉(zhuǎn)換成對象調(diào)用
-
any 與 unknown 的區(qū)別:any 為放棄類型檢查各吨,unknown 為未知類型枝笨。
- any 可以給任意類型變量賦值,nuknown 不可揭蜒。
- any 可以進行任意操作横浑,nuknown 幾乎什么不能干(字符串、對象操作屉更、數(shù)學(xué)運算等)徙融;
-
數(shù)組
-
const arr: (string | number)[] = [0, 'a']
與const arr: string | number[] = 'a'
不同 -
const arr: string[] = ['0', 'a']
與const arr: [string] = ['a']
不同 - 只讀數(shù)組:
arr: ReadonlyArray<string>
、arr: readonly string[]
瑰谜。不能arr: readonly Array<string>-
內(nèi)置只讀對象
type Readonly<T> = { readonly [P in keyof T]: T[P] }
- 所以可以
arr: Readonly<string[]>
欺冀、arr: Readonly<Array<string>>
- 只能訪問不可變更树绩,不可賦值給普通數(shù)組,但接受普通數(shù)組給自己賦值(這不是個 bug 嗎)
-
內(nèi)置只讀對象
-
-
對象
-
const obj: object = { foo: 0 }obj.foo // err;
因為 object 類型下并沒有 foo 屬性 const arr12: { x: number } = { x: 0, y: 3 }; // err const arr12: { x: number } = { x: 0, y: 3 } as { x: number; y: number }; // ok const arr12: { x: number } = { x: 0, y: 3 } as { x: number; [key: string]: number }; //ok const temp = { x: 0, y: 3 }; const arr12: { x: number } = temp; // ok
-
-
函數(shù)
// 指定 this 結(jié)構(gòu) function foo(this: { name: string }, x: number): void { console.log(this.name, x); } foo.prototype.name = "foo"; new foo(3);
- 可以根據(jù)參數(shù)個數(shù)不同重載方法
-
Interface
- 索引簽名
[prop: string]: Type
任意屬性名隐轩、任意屬性個數(shù) - 重載的方法必須全部是不可選(可選)弯汰,不可一些方法可選一些方法不可選
- 只讀屬性或方法
readonly count: number
- 繼承多父接口時掸冤,父接口們的屬性發(fā)生沖突,繼承會報錯,需要在子接口的重寫橡娄,且重寫的屬性需要兼容所有父接口(沖突的屬性的類型)
interface A { name: string } interface B { name: number } interface C extends A, B { name: any }
- 索引簽名
-
類型別名 Type
-
type A = { name: string }; type B = A & { age: number }
效果與繼承一樣 - 與 Interface 的區(qū)別:
- 定義方法:
type foo = (x: number) => void;
、interface foo { (x: number): void }
伏伐; - type 可以使用聯(lián)合類型腿短、元組;interface 可繼承其它 interface 或 class;
- interface 可以聲明合并
interface A { name: string }; interface A { age: string };
- 定義方法:
-
-
類
-
const A = class B {}
“B” 只供類內(nèi)部使用 - 使用非空類型斷言“!”來通知編譯器該成員變量已經(jīng)進行初始化
class { a!: number; constructor() {} }
称勋,以逃避編譯器的檢查 - 可以使用
[key: string]: number;
定義屬性胸哥,但它不符合 ts 的宗旨 - 有 public、protected赡鲜、private空厌,主要依靠編譯器的語法檢查(ES10 中添加了 #attr 語法做為私有屬性)
- constructor 可以重載
- 子類重寫父類屬性,只允許放寬银酬,protected 可轉(zhuǎn)為 public嘲更,不可轉(zhuǎn)為 private
- 子類的構(gòu)造函數(shù)必須先調(diào)用 super()再使用 this,且(ts 中)super()必須是第一個語句
- 實例化子類順序:父類屬性 -> 父類構(gòu)造函數(shù) -> 子類屬性 -> 子類構(gòu)造函
- extends 繼承類揩瞪;implements 實現(xiàn)接口赋朦。 類只支持單繼承;implements 可以多實現(xiàn)
- 接口繼承了含有非 public 屬性的類李破,則此接口只能由該類的派生類實現(xiàn)(書 5.15.9 最后)
- static 靜態(tài)屬性(ES10)宠哄,靜態(tài)屬性可繼承(且修改其值不影響父類;不改子類改父類嗤攻,子類也變毛嫉;都改互不影響)
- 抽象類 abstract:不可實例化,可繼承和被繼承妇菱,被繼承的子類可以實例化承粤。抽象屬性不可有具體實現(xiàn),且不能是 private
-
-
泛型
function fn<T>(arg: T): T { return arg; }
闯团,fn<string>('xx')
<定義類型 1辛臊,定義類型 2...>(arg: 使用類型): 使用類型;T 不能是 undefined 和 null
function fn<T1, T2 = string>(arg: T1): T2 {}
偷俭,fn<string, boolean>('xx')
-
泛型約束(extends):T 可以繼承一個類型浪讳,若指定的默認類型,默認類型必須符合繼承的類型(泛型約束)涌萤;實際傳入的類型也必須符合泛型約束
function fn<T extends number = 0 | 1>(arg: T)
淹遵,fn<2 | 3>(3)
-
泛型類描述的是類的實例類型口猜,所以類的靜態(tài)成員中不允許引用類型參數(shù)
class A<T>{ static tag: T; } // 編譯錯誤!靜態(tài)成員不允許引用類型參數(shù)
訪問 tag 時可以使用A.tag
透揣,泛型管不到靜態(tài)屬性
聯(lián)合類型
type T = T1 | T2
滿足一個就行济炎;有同名不同類型的屬性,不可混用辐真。-
交叉類型
type T = T1 & T2
全部滿足才行须尚;有同名不同類型的屬性,簡單類型為 never侍咱,方法則重載耐床。-
“&”相當(dāng)于“×”,而“|”相當(dāng)于“+”
T = (string | 0) & (number | 'a'); = (string & number) | (string & 'a') | (0 & number) | (0 & 'a'); = never | 'a' | 0 | never; = 'a' | 0;
-
-
索引類型
keyof Type
楔脯,keyof any => string|number|symbol
撩轰,keyof unknown => never
- 聯(lián)合類型
keyof(T1 | T2)
共有的屬性名。 - 交叉類型
type T = T1 & T2
全部滿屬性名昧廷。
- 聯(lián)合類型
-
映射對象:
type newT = { readonly [K in keyof oldT]?: oldT[K] }
復(fù)制 oldT 將所有屬性變?yōu)橹蛔x可選的(同態(tài)映射)-
[K in string]: number
等于[prop: string]: number
- 同態(tài)映射會默認拷貝源對象類型中所有屬性的 readonly 修飾符和“?”修飾符
- 使用變量中轉(zhuǎn)過的
keyof oldT
不再屬于同態(tài)映射堪嫂,所以不會拷貝原屬性的修飾符:type EK = keyof oldT; type newT = { [K in EK]?: oldT[K] }
-
type Required = { +readonly [K in keyof T]-?: T[K] }
刪除 ? 修飾符添加 readonly 修飾符;type T = { a?: string | undefined | null; readonly b: number | undefined | null; } // { // readonly a: string | null; // readonly b: number | undefined | null; // } type RequiredT = Required<T>; // - 號可以過刪除 undefined木柬,but not null
- 更多類型的映射方式參見 6.6.4 同態(tài)映射對象類型
-
-
條件類型
-
type T = true extends boolean ? string: number;
=> string - 裸類型參數(shù)(Naked)即沒有任何裝飾([]皆串、{}等)的類型參數(shù);
- 如果
extends NakedType
(分布式條件類型)則條件展開眉枕; - 內(nèi)置工具類型:
Exclude<T, U>
恶复、Extract<T, U>
、Non-Nullable<T>
從聯(lián)合類型中過濾速挑、挑選指定類型寂玲、創(chuàng)建非空類型; -
T extends Array<infer U> ? U : never
推斷梗摇;inferType<number[]> => number
-
-
內(nèi)置工具類型(6.8 那么多,怎么可能記得紫胄怼)
-
Partial<T>
=>{ [K in keyof T]?: T[K] }
復(fù)制一份并全為可選 -
Required<T>
=> -? 全為必選 -
Readonly<T>
=> 全為只讀 -
Record<AttrNames, AttrType>
=>{ name1: AttrType, name2: AttrType... }
-
Pick<T, AttrNames>
=> 從 T 中選 AttrNames 這些屬性創(chuàng)建新類型 -
Omit<T, AttrNames>
=> Pick 的互補伶授,AttrNames 為要忽略的屬性 -
Exclude<Ks, AttrNames>
=> 與 Omit 的區(qū)別為 Ks = keyof T -
Extract<Ks, AttrNames>
=> 與 Pick 的區(qū)別為 Ks = keyof T -
NonNullable<T>
=> 從類型 T 中剔除 null 類型和 undefined 類型并構(gòu)造一個新類型 -
Parameters<fnT>
=> 使用方法類型 fnT 的參數(shù)類型構(gòu)造一個元組類型Parameters<(s: string) => void> // [string]
-
ConstructorParameters<fnT>
=> 構(gòu)造函數(shù)ConstructorParameters<new (s: string) => void> // [string]
-
ReturnType<fnT>
=> 方法類型的返回值類型ReturnType<(s: string) => void> // void]
-
InstanceType<T>
=> 獲取構(gòu)造函數(shù)的返回值類型,即實例類型 -
ThisParameterType<fnT>
=> 方法中 this 參數(shù)的類型(需要啟用“--strictFunctionTypes”編譯選項) -
OmitThisParameter<T>
=> 剔除 this 參數(shù) -
ThisType<T>
=> 不創(chuàng)建新類型 用于定義對象字面量的方法中 this 的類型???(需要啟用“--noImplicitThis”)
-
類型查詢流纹,對 typeof 進行了擴展
const arg2: typeof arg1 = arg1
-
類型斷言
<T>expr
-
<HTMLElement>ele
=ele as HTMLElement
- 強制類型轉(zhuǎn)換
ele as unknown as T
糜烹,復(fù)雜類型之間的類型斷言,編譯器可能會無法識別出正確的類型漱凝,因此錯誤地拒絕了類型斷言操作 -
value as const
疮蹦,只讀字面量類型,如果 value 是個多級對象茸炒,只讀作用于所有深度 - 非空類型斷言“!”
-
-
類型細化
- 類型守衛(wèi):typeof愕乎、instanceof阵苇、in
- 斷言函數(shù):
function fn(x): asserts x is T
orasserts x
- 前者,只有 x 是 T 類型時感论,該函數(shù)才會正常返回绅项,且此邏輯要由開發(fā)者自己實現(xiàn);
- 后者比肄,只有 x 為真時快耿,才會正常返回,邏輯由開發(fā)者實現(xiàn)芳绩;
- 斷言函數(shù)沒有返回值掀亥。用于控制流程;
類型深入(第 7 章)
字面量類型:原始類型的子類型妥色,比如具體的值
never <: undefined <: null <: 其它類型
函數(shù)類型重載:S 是 T 的子類型搪花,且 T 存在函數(shù)重載,那么 T 的每一個函數(shù)重載必須能夠在 S 的函數(shù)重載中找到與其對應(yīng)的子類型
結(jié)構(gòu)化子類型:根據(jù)對象的屬性名和類型垛膝,直接判斷兩對象間的父子關(guān)系
實例的父子關(guān)系只檢查非靜態(tài)鳍侣、非構(gòu)造的成員屬性,包含另一個的所有成員屬性就是它是子類型吼拥。若有 protected 屬性要求兩者間有繼承關(guān)系
泛型函數(shù):noStrictGenericChecks 時只檢查參數(shù)個數(shù)不檢查類型倚聚,否則,兩者間互相帶入對方的類型進行推斷
子類型在理論上可以賦值給父類型凿可,any惑折、數(shù)值型枚舉除外。
ts 命名空間基于自執(zhí)行函數(shù)實現(xiàn)通過“tsconfig.json”中 files 配置文件能夠定義文件間的加載順序
三斜線指令
/// <reference path="a.ts" />
定義文件間依賴(a.ts 不在 tsconfig.json 中定義也會被打包)-
模塊
- CommonJS:nodejs枯跑,同步加載文件惨驶,不適用于瀏覽器
- AMD:異步模塊定義,requirejs
- UMD:通用模塊定義敛助,基于上是 vue 打包后的樣子
export ... from xxx
其它模塊的導(dǎo)出作為當(dāng)前模塊的導(dǎo)出粗卜,xxx 中的功能不能在當(dāng)前文件中使用。空導(dǎo)出使用是的模塊的副作用
類型導(dǎo)入導(dǎo)出
import [type|interface] ...
,export [type|interface] ...
如果一個模塊只導(dǎo)出類型纳击,其副作用在打包時會被刪除import(uri) => Promise<Module>
異步引入模塊(es 語法续扔,非 ts 獨有)。Module 的值相當(dāng)于import * as Module from 'uri'
編譯
tsc index.ts --module [None|CommonJS|AMD|UMD|ES6|ES2020....]
-
外部類型聲明
- *.d.ts
- declare const devicePixelRatio: number;
- declare module 'io' { export function readFile(uri: string): string; }
- declare module 'jquery'; 放棄對 jquery 插件進行類型檢查
- 第三方插件沒有 d.ts 聲明文件時可以在 npm 官網(wǎng)使用
@types/插件名
搜索焕数,或者于https://www.typescriptlang.org/dt/search?search=jquery
處查找 - package.json 文件中新增了 typings|types 屬性指定包的聲明文件纱昧,未指定默認為 main 同名同位置文件
-
模塊解析
ts --moduleResolution [Classic|Node]
-
--baseUrl: '.'
非相對模塊導(dǎo)入的基準路徑 -
path: { "b": ["bar/b"], "@utils": ["./src/utils"] }
非相對模塊導(dǎo)入的映射路徑 -
rootDirs: ['src1', 'src2']
使用不同的目錄創(chuàng)建出一個虛擬目錄,相對模塊導(dǎo)入的基準路徑 - 外部模塊聲明
typings.d.ts
(vue 中訪問不到 d.ts 中的自定義 type堡赔,目前看來是 eslint 的問題识脆,沒有解決)
-
-
聲明合并
- 接口合并:同名 interface 會被合并成一個。屬性出現(xiàn)同名不同類型會報錯;方法出現(xiàn)同名不同類型會生成重載(入?yún)樽置媪康膬?yōu)先級高灼捂,后聲明的優(yōu)先級高)离例;
- 枚舉合并:同名 enum 會被合并成一個。有相同值的枚舉合并報錯纵东;const 與非 const 枚舉合并報錯粘招;
- 類合并:同名 class 不能合并。 但 declare class 可以和 interface 合并偎球,當(dāng)做對 class 類的擴展
-
擴充模塊聲明
import { A } from './a' declare module './a' { // 正確 interface A { other: string; } // 錯誤洒扎,不能擴充頂層聲明 interface B { name: string; } } const a: A = { other: 'A中沒有的屬性' }
如果是
import './a'
無法對其擴充,interface 為必須 -
擴充全局聲明
declare global { interface Window { other: string; } }
ts 配置
-
使用 tsc 指令時:
路徑中的空格:
tsc 'file name.ts'
ortsc file\ name.ts
多個文件:使用空格分割衰絮,或者使用通配符
-w
:文件變更時自動重新編譯-
嚴格類型:
--strict
為總開關(guān):- noImplicitAny 禁用隱式 any 類型轉(zhuǎn)換
- strictNullChecks 頂上
- strictFunctionTypes 頂上
- strictBindCallApply 對 call apply bind 中的 this 參數(shù)進行類型檢查
- strictPropertyInitialization 類屬性初始化檢查
- noImplicitThis 頂上
- alwaysStrict 啟用 js
use strict
-
tsconfig.json 若一個目錄中存在此文件袍冷,該目錄將被編譯器視作 TypeScript 工程的根目錄(或使用-p/--project tsconfig.json 來指定編譯配置)。
tsc --init --locale zh-CN
當(dāng)前目錄初始化一個 tsconfig.jsoncompilerOptions > listFiles
打印出參與本次編譯的文件列files
不支持模糊匹配猫牡,頂上include
定義編譯文件列表胡诗,常與exclude
一起用,以剔除 include 中不需要的文件淌友。它們都支持模糊匹配compilerOptions > typeRoots
默認指向 node_modules/@types煌恢;值為相對于 tsconfig.json 的路徑列表compilerOptions > types
功能和 typeRoots 相同,但值為具體文件而非目錄compilerOptions > isolatedModules
當(dāng)編譯器發(fā)現(xiàn)無法被正確處理的語言結(jié)構(gòu)時給出提示tsc --showConfig
不編譯代碼震庭,只顯示當(dāng)前配置extends
可以繼承其它 tsconfig.json
-
工程引用:使用 references 引用其它工程瑰抵,被引用的工程需要開啟 composite
{ "references": [ { "path": "../dll1" }, { "path": "../dll2/tsconfig.release.json" } ] }
{ "compilerOptions": { "composite": true, "declaration": true, // 必須 "declarationMap": true // 必須,對d.ts生成sourceMap器联,轉(zhuǎn)到定義時跳入真實代碼位置而不是d.ts // 如果設(shè)置了 include 或 files二汛,所有源文件必須都包含在內(nèi) } }
-
tsc [--build|-b] --watch
查找工程引用,構(gòu)建引用(如果有更新)-
--verbose
:打印構(gòu)建日志拨拓; -
--dry
:只要日志但不真構(gòu)建肴颊; -
--clean
:清理輸出文件; -
--force
:強制重新編譯渣磷; -
--noEmit
:僅類型檢查婿着,不編譯;
-
solution 工程:在所以工程的父目前創(chuàng)建 tsconfig.json醋界,references 引用所有工程祟身,include、files 設(shè)為空數(shù)組物独。
-
-
Javascript 類型檢查
tsc index.js --allowJs --outDir dist
編譯 js 文件--allowJs --checkJs
對 js 文件進行類型檢查// @ts-nocheck
對當(dāng)前 js\ts 禁用類型檢查// @ts-check
對當(dāng)前 js\ts 強制開啟類型檢查,無論有無--checkJs
// @ts-ignore
忽略下一行語句的類型檢查-
JSDoc: 基于
/** xxx */
注釋-
@typedef {(number | string )} CompanyID
生成自定義類型 CompanyID -
@type {CompanyID}
定義變量類型 -
@param {CompanyID} cpyId - 公司id
定義參數(shù)類型 -
@param {CompanyID} [cpyId=0] - 公司id
默認參數(shù) @return {CompanyID}
-
@extends {FatherClass}
定義繼承的基類 -
@public
氯葬、@protected
挡篓、@private
、@readonly
等等
-
TS 人家的例子
轉(zhuǎn)譯器
tsc index.js --allowJs --target ES5 --outFile index.es5.js
將 js 文件編譯為 es5 語法-
babel7 提供了內(nèi)置的 ts 支持
- @babel/core 核心轉(zhuǎn)譯功能
- @babel/cli 命令行工具
- @babel/preset-env 按需編譯和按需打補丁
- @babel/preset-typescript ts 支持包
npm init --yes
cnpm i typescript -D
cnpm i @babel/core @babel/cli @babel/preset-env @babel/preset-typescript -D
npx babel [開發(fā)目錄] --out-dir lib --extensions \".ts,.tsx\" --source-maps inline
// .babelrc.json
{
"presets": ["@babel/env", "@babel/typescript"]
}
名詞
頂端類型:通用類型,超類型官研,ts 中指 any秽澳、unknown
尾端類型:never