TypeScript學習記錄整理(待繼續完善)
typescript
Interface
對物件的形狀進行描述
interface Person {
name: String
//可選屬性
age?: number
//只讀屬性
readonly id: number
}
也可以用來描述函式型別
interface Random {
(x:number, y:number):number
}
const add:Random =(num1,num2)=>{
return num1+num2
}
自定義屬性
interface Key { [propName: string]:string } //看起來像類陣列 interface LikeArray { [propName: number]:string }
類
封裝,繼承,多型
//封裝
class Person {
//屬性
name: string
//構造器
constructor(name: string) {
this.name = name
}
//方法
speak() {
pass
}
}
//例項化
const p1 = new Person()
//使用
p1.name
p1.speak()
//繼承 extends class Student extends Person { study() { c.log(`${this.name} needs study`) } } const s1 = new Student('lin) s1.study //Student類如果還有自己定義的自己的屬性,就需要super關鍵字 class Student extends Person { grade: number constructor(name:string,grade:number) { super(name) this.grade = grade } study() { c.log(`${this.name} needs study`) } }
//多型-子類重寫父類方法
class Student extends Person {
speak() {
pass
}
}
public,private,protected,static
- public-共有,預設是public,類例項,子類等都可以使用
- private-只屬於類自己,其例項和其子類都不可訪問
- protected-例項不可訪問,其子類繼承可訪問
- static-可理解為類上的常量,類本身可以訪問,例項和子類不能訪問
抽象類-abstract-只能被繼承,不能被例項化的類
- 不允許被例項化
- 類中的抽象方法必須被子類實現
this型別
- 類的成員方法直接返回this,可以實現方法鏈式呼叫
class StudyStep {
step1() {
console.log('listen')
return this
}
step2() {
console.log('write')
return this
}
}
const s = new StudyStep()
s.step1().step2()
- 子類成員方法返回this,可實現父類子類方法靈活呼叫
class StudyStep {
step1() {
console.log('listen')
return this
}
step2() {
console.log('write')
return this
}
}
class MyStudyStep extends StudyStep {
next() {
console.log('before done, study next!')
return this
}
}
const m = new MyStudyStep()
m.step1().next().step2().next() // 父型別和子型別上的方法都可隨意呼叫
interface和類的關係
interface不僅可以對物件的形狀進行描述,也可以來約束class
implements-定義約束後,class必須滿足介面上的所有條件,用於約束類例項上的屬性和方法
interface MusicInterface {
playMusic(): void
}
class Car implements MusicInterface {
//implements了interface後必須要滿足條件
playMusic() {}
}
class Cellphone implements MusicInterface {
playMusic() {}
}
約束建構函式和靜態屬性
interface CircleStatic {
new (radius: number): void
pi: number
}
const Circle:CircleStatic = class Circle {
//pi在CilcleStatic中有定義,要定義為靜態屬性
static pi: 3.14
public radius: number
//radius在interface中型別為number,constructor入參型別不對會報錯
public constructor(radius: number) {
this.radius = radius
}
}
列舉-enum
數字遞增 & 反向對映
//數字遞增
enum Direction {
//預設
Up,
//1.Up = 6
Down,
Left,
Right
}
console.log(Direction.Up) //0 其餘123遞增
> 1的情況: 數字遞增從6開始,也可以每個元素都設定初始值
//反向對映
console.log(Direction[0]) //'Up'
console.log(Direction[6]) //'Up'
//原始碼解析-被編譯後的程式碼
var Direction;
(function (Direction) {
//js中的賦值運算子返回被賦予的值
//所以下面Direction['Up'] = 6 返回的6
//相當於執行 Direction[6] = 'Up'
Direction[Direction["Up"] = 6] = "Up";
Direction[Direction["Down"] = 7] = "Down";
Direction[Direction["Left"] = 8] = "Left";
Direction[Direction["Right"] = 9] = "Right";
})(Direction || (Direction = {}));
計算成員
enum FileAccess {
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
}
console.log(FileAccess.Read) // 2 -> 010
console.log(FileAccess.Write) // 4 -> 100
console.log(FileAccess.ReadWrite) // 6 -> 110
字串列舉
常量列舉
- const 編譯出來的js程式碼會更簡潔,提高效能
- 常量列舉不允許包含計算成員
型別
1.型別推論
- 定義時不賦值會被推斷為any型,之後隨便賦值都不會報錯
let aaa
a = 22
a = 'lin'
- 初始化變數,定義時賦值但沒指定型別
let b = 'sss'
b = 22 //報錯,初始化自動推導為string型別,賦值為number型別會報錯
- 函式預設形參
function func(a = 22) {
console.log(a)
return a
}
func('saa') //自動推導為number,傳了string會報錯
- 函式返回值
- 無return自動推導為void
- 有return比如上面的例子,會自動推導為number,如果給func寫了型別比如說string會報錯
2.內建型別
- 八種基本型別
- string number boolean null undefined object bigint symbol
- ecmascript內建物件
- Array Date Error RegExp等
const nums: Arrat<number> = [1,2,3] const nums: number[] =[1,2,3]
- DOM 和 BOM
- HTMLElement NodeList MouseEvent等
3.聯合型別 |
希望一個變數可以支援多種型別
let num: number | string
4.交叉型別 &
對物件形狀進行擴充套件,使用&
interface Person {
name: string,
age: number,
}
//Student在Person 有name和age屬性的基礎上還想要有grade屬性,和繼承類似
type Student = Person & {grade: number}
const s1:Student
s1.name/age/grade
5.類型別名
type Name = string // 基本型別
type arrItem = number | string // 聯合型別
const arr: arrItem[] = [1,'2', 3]
type Person = {
name: Name
}
type Student = Person & { grade: number } // 交叉型別
type Teacher = Person & { major: string }
type StudentAndTeacherList = [Student, Teacher] // 元組型別
const list:StudentAndTeacherList = [
{ name: 'lin', grade: 100 },
{ name: 'liu', major: 'Chinese' }
]
type和interface區別
- 共同點: 都可以定義一個物件或函式,都允許繼承,interface使用extends實現繼承,type使用交叉型別實現繼承
-
不同點:
- 設計目的不同,interface是為了對物件的形狀進行描述,type是類型別名,為了讓ts寫起來更清晰
- type可以宣告基本型別,聯合型別,交叉型別,元組,interface不行
- interface可以合併重複宣告,type不可以,會報錯
6.型別保護
//返回入參是聯合型別的length,但是number沒有會報錯,所以用typeof處理
function getLength(arg: number | string): number {
if(typeof arg === 'string') {
return arg.length
} else {
return arg.toString().length
}
}
7.型別斷言 as
值 as 型別
8.字面量型別
type ButtonSize = 'mini' | 'small' | 'normal' | 'large'
type Sex = '男' | '女'
泛型
解決輸入輸出要一致的問題
泛型基本使用
- 處理函式引數
//使用<T>,相當於佔位符,在使用時定義的型別*會像引數一樣傳入*,它會原封不動地輸出
function print<T>(arg:T):T {
console.log(arg)
return arg
}
//使用時可以靠自己直接定義好T或者靠型別推斷
eg:
print<string>('ddd')
print('aaa')
- 預設引數
//預設就是number型別了
interface Iprint<T = number> {
(arg: T):T
}
- 處理多個函式引數
//傳入一個只有兩個元素的元組,交換元組位置後返回
function swap<T, U>(tuple: [T, U]): [U, T]{
return [tuple[1], tuple[0]]
}
- 函式副作用操作
//根據不同的url請求返回不同型別的資料
interface UserInfo {
name: string
age: number
}
function request<T>(url:string): Promise<T> {
return fetch(url).then(res => res.json())
}
request<UserInfo>('user/info').then(res =>{
console.log(res)
})
約束泛型
和interface結合類約束泛型
interface ILength {
length: number
}
function printLength<T extends ILength>(arg: T): T {
console.log(arg.length)
return arg
}
//使用
const str = printLength('Lin')
const arr = printLength([1,2,3])
const obj = printLength({length: 10})
//這個也驗證了interface的duck typing,只要你有length屬性,都符合約束,那不管你是什麼,都沒問題
泛型應用
可以在定義函式,介面或者類的時候,不預先指定具體型別,等到使用的時候再指定
- 泛型約束類
class Stack<T> {
private data: T[] = []
push(item:T) {
return this.data.push(item)
}
pop():T | undefined {
return this.data.pop()
}
}
//入棧出棧都是同一種類型
const s1 = new Stack<number>()
const s2 = new Stack<string>()
//注意,泛型無法約束類的靜態成員,給pop方法定義static關鍵字,會報錯
- 泛型約束介面
interface IKeyValue<T, U> {
key: T
value: U
}
const k1:IKeyValue<number, string> = { key: 18, value: 'lin'}
const k2:IKeyValue<string, number> = { key: 'lin', value: 18}
- 泛型定義陣列
const arr: number[] = [1,2,3]
const arr: Array<number> = [1,2,3]
索引型別
從物件中抽取指定的屬性的值拼接成陣列
const userInfo = {
name: 'lin',
age: '18',
}
function getValues(userInfo: any, keys: string[]) {
return keys.map(key => userInfo[key])
}
// 抽取指定屬性的值
console.log(getValues(userInfo, ['name','age'])) // ['lin', '18']
// 抽取obj中沒有的屬性:
console.log(getValues(userInfo, ['sex','outlook'])) // [undefined, undefined]
物件中沒有sex和outlook屬性,但是ts編譯器也沒有報錯,要解決這個問題,需要對這種情況做型別約束,實現動態屬性的檢查
keyof(索引查詢)
keyof可以用於獲取某種型別的所有鍵,其返回型別是聯合型別
interface IPerson {
name: string;
age: number;
}
type Test = keyof IPerson; // 'name' | 'age'
T[K](索引訪問)
T[K]-表示介面T的屬性K所代表的型別
interface IPerson {
name: string;
age: number;
}
let type1: IPerson['name'] // string
let type2: IPerson['age'] // number
extends(泛型約束)
泛型變數通過extends繼承某個型別,獲得某些屬性
interface ILength {
length: number
}
function printLength<T extends ILength>(arg: T): T {
console.log(arg.length)
return arg
}
const str = printLength('lin')
const arr = printLength([1,2,3])
const obj = printLength({ length: 10 })
const num = printLength(10) // 報錯,Argument of type 'number' is not assignable to parameter of type 'ILength'
檢查動態屬性
- 定義泛型 T、K,用於約束 userInfo 和 keys
- 為 K 增加一個泛型約束,使 K 繼承 userInfo 的所有屬性的聯合型別, 即K extends keyof T
function getValues<T, K extends keyof T>(userInfo: T, keys: K[]): T[K][] {
return keys.map(key => userInfo[key])
}
對映型別
將一個型別對映成另一個型別
in操作符,對聯合型別實現遍歷
Partial,Partial可將T所有屬性對映為可選的
interface IPerson {
name: string
age: number
}
type IPartial = Partial<IPerson>
//之前初始化的時候name和age都必須要傳
let p1: IPartial = {}
- 實現原理: 通過in和keyof
- [p in keyof T] 遍歷T上所有的屬性
- ?: 設定為屬性為可選的
- T[P] 設定型別為原來的型別
type Partial<T> = {
[P in keyof T]?: T[P]
}
Readonly-Readonly
將T的所有屬性都對映為只讀
interface IPerson {
name: string
age: number
}
type IReadOnly = Readonly<IPerson>
let p1: IReadOnly = {
name: 'lin',
age: 18
}
- 實現原理:和Partial幾乎一樣
type Readonly<T> ={
readonly [P in keyof T]: T[P]
}
pick-抽取物件子集,挑選一組屬性並組成一個新的型別
interface IPerson {
name: string
age: number
sex: string
}
type IPick = Pick<IPerson, 'name' | 'age'>
let p1: IPick = {
name: 'lin',
age: 18
}
- 實現原理:第一個引數是要抽取的目標物件,第二個引數K:有個約束是K一定要是來自T所有屬性字面量的聯合型別
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
Record
前面三種只作用於obj屬性而不會引入新的屬性,稱為同態
record是會建立新屬性的非同態對映型別
interface IPerson {
name: string
age: number
}
type IRecord = Record<string, IPerson>
let personMap: IRecord = {
person1: {
name: 'lin',
age: 18
},
person2: {
name: 'liu',
age: 25
}
}
- 實現原理:
- 第一個引數可以傳入繼承於any的任何值
- 第二個引數,作為新建立物件的值,被傳入
type Record<K extends keyof any, T> = {
[P in K]: T
}
條件型別
T extends U ? X : Y
Exclude
Exclude 意思是不包含,Exclude<T, U> 會返回 聯合型別 T 中不包含 聯合型別 U 的部分
type Test = Excludes<'a'|'b'|'c','a'>
//返回的Test就是 ‘b’|'c'
-實現原理:
type Exclude<T, U> = T extends U ? never : T
never表示一個不存在的型別
never與其他型別的聯合後,為其他型別
type Test = string | number | never
就是type Test = string | number
Extract
Extract<T, U>提取聯合型別 T 和聯合型別 U 的所有交集
type Test = Extract<'key1' | 'key2', 'key1'>
Test型別就是key1
- 實現原理:
type Extract<T,U> = T extends U ? T: never
工具型別
上文的索引型別,對映型別和條件型別都是工具型別
omit
- Omit<T,U> 從型別T中剔除U中的所有屬性
NonNullable
- NonNullable
用來過濾型別中的null及undefined型別
Parameters
- 獲取函式的引數型別,將每個引數型別放在一個元組裡
ReturnType
- 獲取函式的返回值型別