基礎(chǔ)數(shù)據(jù)類型
JS的八種內(nèi)置類型
let str: string = 'abc';
let num: number = 123;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol('me');
注:默認(rèn)情況下零渐,null和undefined是所有類型的子類型,可以賦值給其他類型走贪。
如果tsconfig.json中指定 “strictNullChecks”: true,則null和undefined只能賦值給void。
Array
兩種定義定義(聲明字符串?dāng)?shù)組)
let arr: string[] = ['a', 'b'];
let arr2: Array<string> = ['a', 'b']
定義聯(lián)合類型數(shù)組
let arr:(number | string)[]; // 既可以存儲(chǔ)數(shù)字,也可以存儲(chǔ)字符串
定義指定對(duì)象成員的數(shù)組
interface Arrobj{
name: string,
age: number
}
let arr: Arrobj[] = [{name: 'John', age: 22}]
Tuple(元祖)
數(shù)組一般值類型相同效斑,當(dāng)需要數(shù)組存儲(chǔ)不同類型值時(shí),可以通過元祖指定元素個(gè)數(shù)和類型柱徙。這是TS特有的類型缓屠,工作方式類似于數(shù)組。
定義元祖
let x: [string, number] = ['abc', 123]
元祖類型的結(jié)構(gòu)賦值
let [str, id] = x
console.log(str, id) // ’abc', 1
元祖類型的可選元素
let optionTuple: [string, boolean?]; // 以 ? 結(jié)尾的元素為可選元素
optionTuple = ['xxx'] // 賦值時(shí)可選元素可以不賦值
元祖類型的剩余元素
// 接受第一個(gè)元素number類型外护侮,其余參數(shù)為string類型
let restTuple: [number, ...string[]];
restTuple = [666, 'xxx', 'aaa', 'ccc']
只讀類型元祖
let readOnlyTuple: readonly[number, string] = [10, 'xxx'] // 進(jìn)行修改操作會(huì)拋出異常
void
void表示沒有任何類型敌完,和其它類型是平等關(guān)系,不能直接賦值
let a: void;
let b: number = a; // Error
never
never表示永不存在的值的類型羊初。
兩種值永不存在的情況:
- 如果一個(gè)函數(shù)執(zhí)行時(shí)拋出了異常滨溉,那這個(gè)函數(shù)永遠(yuǎn)不存在返回值。
- 函數(shù)中無限循環(huán)的代碼长赞,永遠(yuǎn)不存在返回晦攒。
注:never類型同null 和 undefined 一樣,也是任何類型的子類型得哆,可以賦值給任何類型勤家。但是沒有類型是never子類型,沒有值可以賦值給never柳恐,never只能是作為類型使用伐脖。
any
在TS中,任何類型都可以被歸為any類型乐设,這讓any類型成為了類型系統(tǒng)的頂級(jí)類型讼庇。
普通類型賦值有類型限定,any類型賦值可以被賦值為任意類型近尚。any類型的數(shù)據(jù)訪問任何屬性蠕啄,調(diào)用任何方法都是被允許的。
聲明變量時(shí)未指定類型時(shí),會(huì)被識(shí)別為any類型歼跟。
注:為了開發(fā)規(guī)范和媳,盡量不要用any。
unknown
為了解決any問題哈街,TypeScript3.0引入了unknown類型留瞳。它與any一樣,所有類型都可以分配給unknown骚秦。
unknown與any的最大區(qū)別:任何類型的值都可以賦值給any她倘,同時(shí)any類型的值也可以賦值給任何類型。unknown則任何類型值都可以賦值給它作箍,但unknown只能賦值給unknown和any硬梁。
function getDog() {
retrun 'bobi';
}
const dog: unknown = {hello: getDog};
dog.hello(); // Error unknown不縮小類型的情況下,無法執(zhí)行任何操作
這種機(jī)制起到了預(yù)防性胞得,要求我么必須縮小類型荧止,可以使用typeof、類型斷言等方式來縮小未知范圍阶剑。
function getDogName() {
let x: unknown;
return x;
}
const dogName = getDogName()
// 直接使用
const upName = dogName.toLowerCase(); //Error 無法通過編譯
// typeof
if(typeof dogName === 'string') {
const cuName = dogName.toLowerCase();
}
// 類型斷言
const crName = (dogName as string).toLowerCase();
Number罩息、String、Boolean个扰、Symbol
小寫字母開頭的 number瓷炮、string、boolean递宅、symbol 是原始類型娘香;
首字母大寫的類型是相應(yīng)原始類型的包裝對(duì)象,即對(duì)象類型办龄。
注:原始類型兼容對(duì)應(yīng)的對(duì)象類型烘绽,對(duì)象類型不兼容對(duì)應(yīng)的原始類型。銘記不要使用對(duì)象類型來注解值的類型俐填。
Function
函數(shù)聲明
function sum(x: number, y: number): number {
return x + y;
}
函數(shù)表達(dá)式
let mySum: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y;
}
使用接口定義函數(shù)
interface SearchFunc {
(source: string, subString: string): boolean;
}
let searchStr: SearchFunc = (source, str) {
return source.indexOf(str) > -1
}
可選參數(shù)安接、參數(shù)默認(rèn)值、剩余參數(shù)
function buildInfo(firstName: string, lastName: string = 'zhang', sex?: number, ...otherInfo: any[]) {
console.log(lastName + '' + firstName, sex ? '男同學(xué)': '女同學(xué)', '是' + otherInfo.join(''))
}
buildInfo('san', undefined, 1, '高三', '理科生') // zhangsan男同學(xué)是高三理科生
函數(shù)重載
通常函數(shù)會(huì)根據(jù)不同類型參數(shù)返回不同類型結(jié)果英融,當(dāng)TS開啟 noImplicitAny 配置項(xiàng)時(shí)盏檐,會(huì)提示參數(shù)具有隱式any類型,需要對(duì)不同類型做不同處理驶悟。
function add(x: number, y: number): number;
function add(x: string, y: string): string;
type Types = number | string
function add(x: Types, y: Types) {
if(typeof x === 'string') {
return x.toString() + y.toString()
}
return x + y;
}
類型推斷
為了方便開發(fā)中不需要對(duì)基礎(chǔ)類型進(jìn)行類型注解胡野,TS在很多情況下,會(huì)根據(jù)上下文環(huán)境自動(dòng)推斷出變量的類型痕鳍。const類型推斷會(huì)被推斷為[字面量類型](# 字面量類型)
let str = 'this is string'; // 等價(jià)于 let string: string = 'this is string'
// 需要注意const定義的變量
const str = 'this is string'; // 此時(shí)推斷為字面量類型'this is string'硫豆,不等價(jià)于 const str: string = 'this is string'
在TS中龙巨,具有初始化值的變量、有默認(rèn)值的函數(shù)參數(shù)熊响、函數(shù)返回的類型都可以根據(jù)上下文推斷出來旨别。
如果定義的時(shí)候沒有賦值,不管之后有沒有賦值汗茄,都會(huì)被推斷為any類型而完全不被類型檢查:
let num;
num = 'seven';
num = 7;
類型斷言
有時(shí)會(huì)有部分情況秸弛,有一個(gè)實(shí)體具有比它現(xiàn)有類型更確切的類型,我們需要直接以指定類型處理這個(gè)實(shí)體剔难,來通過編譯胆屿。類似于其他語(yǔ)言的類型轉(zhuǎn)換奥喻,只在編譯階段起作用偶宫。
const arrayNumber: number[] = [1,2,3,4];
// 直接返回大于2的結(jié)果,有可能是undefined环鲤,會(huì)造成編譯錯(cuò)誤
const findThan2: number = arrayNumber.find(num => num > 2);
// 使用類型斷言纯趋,此時(shí)可以肯定結(jié)果是有大于3的值
const findThan3: number = arrayNumber.find(num => num > 3) as number;
類型斷言語(yǔ)法
// 尖括號(hào)
let someValue: any = 'this is string';
let strLength: number = (<string>someValue).length;
// as語(yǔ)法
let strLength: number = (someValue as string).length;
注:推薦使用as語(yǔ)法,尖括號(hào)語(yǔ)法格式會(huì)與react中JSX語(yǔ)法沖突冷离。
非空斷言
在上下文中當(dāng)類型檢查器無法斷定類型時(shí)吵冒,一個(gè)新的后綴表達(dá)式操作符 “!” 可以用于斷言操作對(duì)象是非null和非undefined類型。即 x! 將從 x 值域中排除null和undefined西剥。
let str: null | undefined || string;
str!.toString(); // str需要排除null和undefined類型后剩余string類型才可以調(diào)用toString方法
確定賦值斷言
允許在實(shí)例屬性和變量聲明后放置一個(gè)“痹栖!”(let x!: number),從而告訴TS該屬性會(huì)被明確賦值瞭空。編譯階段不會(huì)報(bào)“賦值前被調(diào)用“的錯(cuò)誤揪阿。
let x: number;
initialize();
console.log(2 * x); // Error 賦值前就被調(diào)用
// 使用確定賦值斷言
let x!: number;
initialize();
console.log(2 * x); // 20
function initialize() {
x = 10
}
字面量類型
在TS中,字面量不僅可以表示值咆畏,還可以表示類型(字符串字面量類型南捂、數(shù)字字面量類型、布爾字面量類型對(duì)應(yīng)的字符串字面量旧找、數(shù)字字面量溺健、布爾字面量擁有與其值一樣的字面量類型)。
let str: 'this is string' = 'this is string';
let num: 1 = 1;
let bool: true = true;
注:字面量類型時(shí)钮蛛,'this is string' 類型是string類型的子類型鞭缭,而string類型不一定是 'this is string'類型。其他字面量類型一樣魏颓。
使用:字面量類型可以用于定義指定值的變量中缚去。由字面量類型組成的聯(lián)合類型可以用于值的范圍限制。如 type status: 'open' | 'close'琼开。
let 和 const 分析
const定義變量缺類型注解的情況下易结,TS會(huì)推斷為字面量類型。
let 定義變量缺類型注解的情況下,TS會(huì)推斷為字面量類型的父類型搞动。
類型拓寬(Type Widening)
所有通過let或var定義的變量躏精、函數(shù)的形參、對(duì)象的非只讀屬性鹦肿,如果滿足指定了初始值且未顯示添加類型注解的條件矗烛,那它們推斷出來的類型就是指定的初始值字面量類型拓寬后的類型,這就是字面量類型拓寬箩溃。
除了字面量類型拓寬瞭吃,TS也對(duì)某些特定類型值也有類似“類型拓寬”的設(shè)計(jì):
對(duì)null和undefined的類型進(jìn)行拓寬,通過let涣旨、var定義的變量如果滿足未顯示聲明類型注解且被賦予了 null 或undefined 值歪架,則推斷出這些變量的類型是any。
let x = null; // 類型拓寬成any
let y = undefined; // 類型拓寬成any
let anyFun = (param = null) => param; // 形參類型是null | undefined
對(duì)于默認(rèn)的類型拓寬霹陡,可以通過添加顯示類型注釋和蚪、使用const斷言等方式來推斷出最窄的類型,沒有拓寬烹棉。
const arr1 = [1,2,3]; // Type is number[]
const arr2 = [1,2,3] as const; // Type is readonly [1, 2, 3]
類型縮性芘(Type Narrowing)
有類型拓寬,就有類型縮小浆洗。通過某些操作符將變量的類型有一個(gè)較為寬泛的集合縮小到相對(duì)較小催束、較明確的集合。
簡(jiǎn)單示例:
// 使用類型守衛(wèi)將函數(shù)參數(shù)的類型從any縮小到明確的類型
let func = (val: any) => {
if(typeof val === 'string') {
return val; // 類型是string
}else if(typeof val === 'number') {
return val; // 類型是number
}
return null;
}
// 使用類型守衛(wèi)將聯(lián)合類型縮小到明確的子類型
let func = (val: string | number) => {
if(typeof val === 'string') {
return val; // 類型是string
}else{
return val; // 類型是number
}
}
注:分支排除法伏社,如聯(lián)合類型排除null(typeof val === 'object') 排除null是錯(cuò)誤的抠刺,需要注意。布爾值取反判斷等特殊值雷同洛口。
聯(lián)合類型
聯(lián)合類型表示取值可以為多種類型中的一種矫付,使用 | 分隔每個(gè)類型。
聯(lián)合類型通常與null第焰、undefined一起使用买优,如函數(shù)參數(shù)str: string | undefined,意味著該形參可以接受undefined挺举。
類型別名
用于給一個(gè)類型起個(gè)新名字杀赢,常用于聯(lián)合類型。
type Message = string | string[]
交叉類型
將多個(gè)類型合并為一個(gè)類型湘纵。常用于將多個(gè)接口合并為一個(gè)類型脂崔,限制指定數(shù)據(jù)滿足多個(gè)接口類型。
type IntersectionType = {id: number, name: string } & {age: number};
const mixed: IntersectionType = {
id: 1,
name: 'name',
age: 18
}
注:合并時(shí)可能出現(xiàn)同名屬性梧喷,會(huì)出現(xiàn)兩種情況:
- 屬性類型不兼容砌左,合并后該屬性的類型就是多個(gè)同名屬性類型的交叉類型脖咐。如都有name屬性,一個(gè)接口為string類型汇歹,一個(gè)接口為number類型屁擅,合并后name屬性就是string & number,也就是never類型产弹,此時(shí)對(duì)name賦值會(huì)報(bào)錯(cuò)派歌,不定義name屬性,則會(huì)提示缺少屬性name痰哨。
- 屬性類型兼容胶果,合并后該屬性的類型就是多個(gè)同名屬性類型的交叉類型的子類型。如都有age屬性斤斧,一個(gè)為number類型早抠,一個(gè)為字面量類型2,合并后age屬性為字面量類型2折欠,此時(shí)age只能賦值為2,贝或。
接口(Interfaces)
用于定義對(duì)象的類型吼过,一般首字母大寫锐秦,且定義的變量跟接口的屬性要保持一致,即賦值的時(shí)候盗忱,變量的形狀必須和接口的形狀保持一致酱床。
可選 | 只讀屬性
可選屬性使用 ? 標(biāo)識(shí)符結(jié)尾,如 name?: string趟佃。
只讀屬性只能在對(duì)象剛剛創(chuàng)建時(shí)修改其值扇谣,readonly name: string。
任意屬性
除必選屬性外闲昭,還可以支持其他屬性罐寨,如接受一些帶限制的擴(kuò)展屬性,key為string類型序矩,值為any類型的屬性鸯绿。
interface Person {
name: string,
age: number,
[propName: string]: any; // 索引簽名的形式實(shí)現(xiàn)屬性擴(kuò)展
}
注:定義任意屬性后,確定屬性和可選屬性的類型都必須是它的類型的子集簸淀。如果任意屬性的類型不包含其他定義屬性(包括可選屬性)的類型瓶蝴,則會(huì)報(bào)錯(cuò)。
繞開額外屬性檢查的方式
當(dāng)對(duì)變量賦值租幕,或是傳遞參數(shù)時(shí)舷手,可能數(shù)據(jù)不僅滿足類型的數(shù)據(jù),還有多余數(shù)據(jù)時(shí)劲绪,可能會(huì)觸發(fā)類型檢查男窟。
-
鴨式辨型法
對(duì)變量直接賦值時(shí)盆赤,會(huì)進(jìn)行類型檢查,而通過變量給變量賦值的形式歉眷,則是會(huì)進(jìn)行類型兼容判斷弟劲,而鴨式辨型法的判斷,只要具有相同屬性姥芥,就會(huì)被認(rèn)為兩個(gè)類型相同兔乞,可以進(jìn)行賦值操作,避開類型檢查凉唐。
interface LabeledValue { label: string } function printLabel(labelObj: labeledValue) { console.log(labelObj.label); } let myObj = {size: 10, label: 'Size 10 Object'}; printLabel(myObj); // OK printLabel({size: 10, label: 'Size 10 Object'}); // Error,形參類型檢查不匹配
-
類型斷言
類型斷言的意義等同于告訴程序庸追,以指定類型對(duì)待數(shù)據(jù),不會(huì)進(jìn)行額外的屬性檢查台囱。
interface Props{ name: string } let myProps: Props = { name: 'xxx', age:18 } as Props;
-
索引簽名
可以直接接收多余的屬性淡溯。(接口任意屬性)
接口與類型別名的區(qū)別
大多數(shù)情況下效果等價(jià),某些特定場(chǎng)景下會(huì)有很大區(qū)別簿训。
interface咱娶、type兩者都可以用來描述對(duì)象或函數(shù)的類型,但是語(yǔ)法不通强品。
Interface
interface Point {
x: number,
y: number
}
interface setPoint{
(x: number, y: number): void
}
Type
type point = {
x: number,
y: number
}
type setPoint = (x: number, y: number) => void
與接口不同膘侮,類型別名還可以用于其他類型,如基本類型(原始值)的榛、聯(lián)合類型琼了、元祖。
// 原始值
type Name = String;
// object
type PartialPointX = {x: number};
type PartialPointY = {y: number};
// 聯(lián)合類型
type PartialPoint = PartialPointX | PartialPointY;
// 元祖
type Data = [number, string];
// dom
let div = document.createElement('div');
type B = typeof div;
注:接口可以定義多次夫晌,類型別名不可以雕薪。多次定義同名接口,會(huì)自動(dòng)合并為單個(gè)接口晓淀。
擴(kuò)展
接口擴(kuò)展接口所袁,可以通過extends繼承,InterfaceA extends InterfaceB
類型別名拓展類型別名凶掰,可以通過 &燥爷,TypeA & TypeB
接口擴(kuò)展類型別名,可以通過 extends锄俄,InterfaceA extends TypeB
類型別名拓展接口局劲,可以通過 &, TypeA & InterfaceB
泛型
當(dāng)一個(gè)變量可以是任意類型奶赠,可以定義為any類型鱼填,但是any類型弊端較大,列舉所有類型情況也不現(xiàn)實(shí)毅戈,就可以通過泛型來實(shí)現(xiàn)苹丸。
實(shí)現(xiàn)一個(gè)函數(shù)愤惰,接受任意類型,返回接收的參數(shù)
function identity<T>(arg: T): T { // T為抽象類型赘理,只有調(diào)用時(shí)才能確定值
return arg;
}
T代表Type宦言,在定義泛型時(shí)通常用作第一個(gè)類型變量名稱,實(shí)際上可以用任意名稱代替商模,通常用T表示而已奠旺。
常見泛型變量代表的意思:
- K(key):表示對(duì)象中的鍵類型;
- V(Value):表示對(duì)象中的值類型施流;
- E(Element): 表示元素類型响疚;
定義泛型時(shí)<>中可以放置多個(gè)類型,用于擴(kuò)展靈活性
function identity<T, U>(value: T, message: U): T{ // 調(diào)用時(shí)傳入了兩個(gè)類型瞪醋,可以一一對(duì)應(yīng)
return value;
}
identity<number, string>(68, 'message');
泛型約束
當(dāng)我們使用泛型而不對(duì)其進(jìn)行任何約束時(shí)TS是會(huì)報(bào)錯(cuò)的忿晕,因?yàn)榇藭r(shí)的泛型理論上是任何類型,不同于any银受,直接訪問屬性或調(diào)用方法會(huì)報(bào)錯(cuò)践盼,除非訪問的都是所有集合公有的。
對(duì)此宾巍,需要對(duì)泛型進(jìn)行約束咕幻,可以用extends關(guān)鍵字實(shí)現(xiàn)。
interface Sizeable{
size: number
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size);
return arg;
}
使用類型繼承蜀漆,而不直接定義形參的類型谅河,是為了可以接收更多的類型咱旱,如果給形參定好了類型确丢,傳入別的類型時(shí)會(huì)丟失類型。
泛型工具類型
TS內(nèi)置了一些常用的工具類型吐限,比如 Partial鲜侥、Required、Readonly诸典、Record和ReturnType等描函。
Partial:將類型的屬性變成可選;
Required:將類型的屬性變成必選狐粱;
Readonly:將類型的屬性變成只讀舀寓;
Record:將指定類型的屬性的值轉(zhuǎn)換為另一個(gè)指定類型;
ReturnType:用來得到一個(gè)函數(shù)的返回值類型肌蜻;
Exclude:將某個(gè)類型中屬于另一個(gè)類型移除掉互墓;
Extract:從指定類型中提取出另一個(gè)類型擁有的屬性類型;
Omit:從一個(gè)類型中提取出指定類型不存在的屬性蒋搜,構(gòu)造一個(gè)新的類型篡撵;
Pick:從某個(gè)類型中挑選一些屬性出來判莉;
NonNullable:過濾類型中的null及undefined類型;
Parameters:擁有獲取函數(shù)的參數(shù)類型組成的元祖類型育谬;
操作符
typeof:獲取變量或者屬性的類型券盅;
keyof:用于獲取某種類型的所有鍵,其返回類型是聯(lián)合類型膛檀;
in:用來遍歷枚舉類型锰镀;
infer:聲明一個(gè)類型變量,存儲(chǔ)自動(dòng)推斷的結(jié)果咖刃,infer只能用于extends語(yǔ)句中互站;