TypeScript 知识点备忘录(个人复习向)

732 阅读10分钟

个人复习使用,并不会对示例代码做详细解析


原始类型


类型语法特点
任意类型any顶级类型:代表所有类型,可以接受其他所有类型,也可被除never类型为所有类型接受。使用时无法进行类型检测。不推荐滥用
void类型void空值,用于函数无返回值
Undefined 类型undefined联合类型默认带有undefined
Null 类型null
Never 类型never表示不存在的值。是所有类型的子类型
字符串类型string包括ES6模板字符串
数字类型number支持二进制、八进制、十进制和十六进制
布尔类型booleantrue 和 false两种值
未知类型unknown顶级类型:可以接受其他所有类型,但本身只能被赋值给 any 类型和 unknown 类型本身。一旦兼容其他类型,就是按照对应类型检测代码
对象object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型
symbol类型symbol通过Symbol构造函数创建

数组


两种定义方式: Type[] 和 Array<Type>   // Type =》 类型

const strArr: string[] = ['1','3']
const strArr2: Array<string> = ['1','3']

let fun = () => 'str'
let arr: {(): string}[]  = [fun]   //存储返回值为字符串类型的函数
let arr2: Array<() => string>  = [fun]

元组


元组类型允许您表示一个已知固定数量的元素的类型的数组

定义 : [Type1, Type2, Type3...] 

const address: [string, number] = ["Street", 99];

枚举


枚举是一种为常量数据提供更友好名称的功能

特点 :1.枚举值有默认编号 从0开始依次递加。可以手动设置初始值2.枚举值是常量类型不能改变3.枚举值可以当作类型使用

数字枚举

enum Num{
    ONE, //0  默认健为0 
    TWO, //1
    THREE = 3 // 3
}
Num.ONE // 0
Num.THREE // 3
Num[0] // ONE

// 使用计算值创建数字枚举
const num = 2
let fun = () => 4

enum Calcuated {
    ONE = num,
    TWO = fun(),
    THREE = 3
}
Calcuated.THREE  //3
Calcuated.ONE   // 2
Calcuated[4]   // TWO

字符串枚举

enum Message {
    Error = 'Sorry, error',
    Success = 'Hoho, success',
    Failed = Error   //使用自身的成员
}
Message.Failed  // Sorry, error
Message.Error   // Sorry, error

异构枚举

同时使用字符串和数字的枚举,称为异构枚举

enum Result {
    Faild = 0,
    Success = 'success'
}

枚举作为类型使用

enum Animals {
    Dog = 1,
    Cat = 'success',
}

let a2:Animals.Cat = Animals.Cat
a2 //'success'

常量枚举

常量枚举,不会被编译为JS对象。某些情况下使用,可以提高性能

函数


函数两要素:定义参数类型和返回值类型

let fun: { (arg1: Type, argN: Type): Type }
等价于
let fun: (arg1: Type, argN: Type) => Type

fun = (arg1: Type, argN: Type):Type => {
    return TypeValue
}

可选参数

可选参数必须位于,必选参数后面

(arg1: Type, optional?: Type) => ReturnType

剩余参数

(arg1: Type, ...Args: Type[]) => ReturnType

默认参数

xx类型 = xx类型的默认值

(arg1: Type = TypeValue , optional: Type = TypeValue) => ReturnType

重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理

1.TS能实现重载的函数只能是使用 function 函数声明定义的的函数
2.重载分为:多次声明的函数头,最后一个实际处理所有函数头的函数

//重载部分
function overloadedMethod(arg1: Type): ReturnType; 
function overloadedMethod(arg1: OtherType, arg2: OtherType): ReturnType;

//具体处理重载的函数
function overloadedMethod(arg1: CommonType, arg2: CommonType): CommonReturnType {} 

对象


在TypeScript中,就像在JavaScript中一样,对象由键值对组成。我们可以为对象键值对分配类型

let userData: { name: string, age: number } = {
    name: 'Max',
    age: 27
  };
  
let complex: { data: number[], output: (all: boolean) => number[] } = {
    data: [100, 3,99, 10],
    output: function(all: boolean): number[] {
      return this.data
   }
};

接口


特点:1.接口里面的的数据必须实现

接口定义

使用 interface 关键字定义

interface MyInterface {
    property: Type;
    optionalProp?: Type; // 可选属性
    optionalMethod(arg1: Type): ReturnType; // 函数定义
}

接口继承

特点:1.可以继承类 
class SomeClass{}
interface Child extends SomeClass {
    
}
2.接口可以单继承,也可以同时继承多个接口
interface Child extends Parent {
    
} 
interface Child extends Parent1, Parent2 {
    
} 

索引签名和只读属性

只读属性:使用 readonly 修饰符修饰的属性。
特点:只可读取,不可修改。

interface MyInterface {
  readonly property: Type;  
}
---------------------------------------------------------------------------------------------------------------------
索引签名: 只有 string 和 number 两种。
注意事项:使用了字符串索引签名。其余成员的返回值类型必须和字符串索引保持一致(或者字符串索引返回值类型兼容其余所有成员)

interface MyInterface {
  [prop: string]: Type;
  [index: number]: Type;
}
  

联合类型


联合类型 表示取值可以为多种类型中的一种。

语法:type | type2 | typeN...

let a: string | number | boolean | {(): string }  
a = 'haha' //ok
a = 1 //ok

类型别名


使用 type 关键字,给一长串代码起一个简洁的名字使用。

type myType = string | number | boolean | {(): string }
let a:myType = 1
a = 'kk'
let b:myType = true

交叉类型


交叉类型是将多个类型合并为一个类型。

interface Sex {
    sex: string
}
interface Person {
    name: string
    age: number
}
type SuperMan = Sex & Person;

const obj: SuperMan = {
    name: 'Bob',
    age: 18,
    sex: 'man'
}

断言


绕过类型检测
1. <类型>值       <Type>Value
2.as 类型     Value as Type

let a = 1;
a.length //error 
(a as unknown as string).length // 断言成未知类型,在通过未知类型断言为string类型。访问length属性就不会报错了

关于unknown类型的解析


类的定义

class SomeClass {
    property: Type;  //定义属性
    readonly readProperty: Type; //定义只读属性
    defaultProperty: Type = 'default value'; //带有默认值的属性
    constructor(property: Type, readProperty: Type) {
        this.property = property    //属性赋值
        this.readProperty = readProperty
  }
   methodProperty: (arg1: Type) => ReturnType; //定义方法
   
    overloadedMethod(arg1: Type): ReturnType; // 也可以在内部定义函数重载
    overloadedMethod(arg1: OtherType): ReturnType;
    overloadedMethod(arg1: CommonTypes): CommonReturnTypes {} 

}

属性修饰符

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public
  • private 修饰的属性或方法是私有的,只能在定义的类中访问。不可在外部和子类中访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class SomeClass {
    public prop: Type;
    private prop1: Type; //不能被子类访问
    protected prop2: Type;
}
class Child extends SomeClass {
    constructor() {
        super()
        this.prop  
        this.prop2
    }
}
----------------------------------------------------------------------------------------------------------------------
可以在 constructor 中使用修饰符,进行简写实例属性。

class SomeClass {
    constructor(public prop: Type)
}
等价于
class SomeClass {
    prop: Type;
    constructor(prop: Type){
        this.prop = prop
    }
}

  • 使用 static 修饰符修饰的方法称为静态方法,它们不能被实例化,而是直接通过类来调用
class MyClass {
    static readonly prop?: Type;
    static staticProperty: Type;
    static fun() {
        
    }
}
MyClass.props
MyClass.fun()

继承

继承:使用 extends 关键字
class Parent {}
class Child extends Parent {}


类实现接口:
使用 implements 关键字

interface MyInterface {}
class Child implements MyInterface {}
--------------------------------------------------------------------------------------------------------------------------
特殊情况:
MyInterface extends Parent{} //如果接口继承了父类
class Child extends Parent implements MyInterface {} //子类需要先继承同一个父类,才能实现接口

存取器

ES6的知识,不过TS也支持设置getters/setters用以改变对属性的读取和赋值行为

使用get和set定义

class Animald {
    constructor(name) {
        this.name = name;
    }
    get name() {
        return 'Jack';
    }
    set name(value) {
        console.log('setter: ' + value);
    }
}

抽象类

抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现

关键字 abstract 来定义抽象类和子类需要必须实现的方法

abstract class Animal {
    constructor(public name) {
        this.name = name;
    }
    abstract sayHi();
    abstract get insideName(): string
    abstract set insideName(value: string)
}
new Animal('haha') //error

class Dog extends Animal {
    sayHi() { }
    insideName:string
} 

类型保护


1. 使用typeof来判断基础类型 string/number/boolean/symbol中的一种

if (typeof item === 'undefined') {
    console.log(item) 
} else { 
    console.log(item())    //error 
}

2.使用 instanceof 来判断 class

class Foo {
  foo = 123;
}

class Bar {
  bar = 123;
}

function doStuff(arg: Foo | Bar) {
  if (arg instanceof Foo) {
    console.log(arg.foo); // ok
    console.log(arg.bar); // Error
  } else {
    // 这个块中,一定是 'Bar'
    console.log(arg.foo); // Error
    console.log(arg.bar); // ok
  }
}

3.使用 in 操作符可以安全的检查一个对象上是否存在一个属性

interface A {
  x: number;
}

interface B {
  y: string;
}

function doStuff(q: A | B) {
  if ('x' in q) {        // in 类似于 for in循环,会遍历对象上的属性
    // q: A
  } else {
    // q: B
  }
}

4.自定义类型保护

// 仅仅是一个 interface
interface Foo {
  foo: number;
  common: string;
}

interface Bar {
  bar: number;
  common: string;
}

// 自己定义的类型保护!
function isFoo(arg: Foo | Bar): arg is Foo { // is 是关键字
  return (arg as Foo).foo !== undefined;
}

// 自己定义的类型保护使用用例:
function doStuff(arg: Foo | Bar) {
  if (isFoo(arg)) {
    console.log(arg.foo); // ok
    console.log(arg.bar); // Error
  } else {
    console.log(arg.foo); // Error
    console.log(arg.bar); // ok
  }
}

5.字面量类型保护:主要用于判断联合类型

type Foo = {
  kind: 'foo'; // 字面量类型
  foo: number;
};

type Bar = {
  kind: 'bar'; // 字面量类型
  bar: number;
};

function doStuff(arg: Foo | Bar) {
  if (arg.kind === 'foo') {
    console.log(arg.foo); // ok
    console.log(arg.bar); // Error
  } else {
    // 一定是 Bar
    console.log(arg.foo); // Error
    console.log(arg.bar); // ok
  }
}

泛型


泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

泛型类

创建一个泛型类
T 是类型占位符, 随便取名字 a b c d....

class Queue<T> {     T 会捕捉输入的类型。这里所有的 T 都是同一个类型
  private data :T[] = [];
  push = (item: T) => this.data.push(item);
  pop = (): T | undefined => this.data.shift();
}

简单的使用
const queue = new Queue<number>();
queue.push(0);
queue.push('1'); // Error:不能推入一个 `string`类型,只有 number 类型被允许
----------------------------------------------------------------------------
可以只给内部成员设置泛型,在使用时传入类型

class Utility {
  reverse<T>(items: T[]): T[] {
    const toreturn = [];
    for (let i = items.length; i >= 0; i--) {
      toreturn.push(items[i]);
    }
    return toreturn;
  }
}
const child = new Utility()
child.reverse<number>([1,2,3,5])
// 借助TS类型推断简写
child.reverse([1,2,3,5])  // T[] === <number>[]

泛型函数

function reverse<T>(items: T[]): T[] {
  const toreturn = [];
  for (let i = items.length - 1; i >= 0; i--) {
    toreturn.push(items[i]);
  }
  return toreturn;
}

const sample = [1, 2, 3];
let reversed = reverse(sample);  // 这里TS会推断为 T 为number类型. T[] === <number>[]

reversed[0] = '1'; // Error
reversed = ['1', '2']; // Error

reversed[0] = 1; // ok
reversed = [1, 2]; // ok

泛型接口

可以同时使用多个泛型

interface Pair<T, U> {
  first: T;
  second: U;
}

let pair:Pair<number,string> = {
    first: 1,
    second: '2'
}

泛型约束

使用 extends 让泛型继承某些条件,来达到约束的作用

interface Lengthwise {
    length: number;
}

传入的参数类型必须带有length属性
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity(7)   // error 
loggingIdentity([1, 2, 3, 4])  //ok
loggingIdentity('111')   //ok
loggingIdentity({     
    length: 6       //ok
})
---------------------------------------------------------------------------------------------------
泛型之间互相约束

function copyFields<T extends U, U>(target: T, source: U): T { //T继承U,所以T必须包含U里面的属性
    for (let id in source) {
        target[id] = (<T>source)[id];
    }
    return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };
let y = { a: 1, b: 2, c: 3};
copyFields(x, { b: 10, d: 20 });   // ok
copyFields(y, { b: 10, d: 20 });   // error

泛型参数的默认类型

当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

keyof 索引类型查询操作符

keyof 连接一个类型,返回由这个类型所有属性名组成的联合类型

interface Info {
    name: string;
    age: number;
}
let infoProp: keyof Info  
//等价于 let infoProp:'name' | 'age'   字面量类型

infoProp = 'name'  //ok
infoProp = 'age'   //ok
infoProp = 'sex'   //error
//----------------------------------------------------------------------------------------------------

interface Type1 {
   a: string;
   b: number;
   c: never;
   d: null;
   e: undefined;
   f: object;
}
type Test3 = Type1[keyof Type1] // 过滤属性值为never,null,undefined的类型
Test3 // stirng | number | object

//----------------------------------------------------------------------------------------------------

declare class Person {
  private married: boolean;
  public name: string;
  public age: number;
}

let publicKeys: keyof Person;
// 等价于 let publicKeys: "name" | "age"
// 只能获取 public(公开的)属性

// ------------------------------------------------------------------------------------------------------

索引访问操作符: []

索引访问操作符会返回属性值的类型:

interface Info {
    name: string;
    age: number;
}
type InfoNameType = Info['name']  //返回name对于的类型:string
等价于
type InfoNameType = string

// K现在是保存T所有的属性,以联合类型形式存在  xx | xx |xx...
function getValue<T, K extends keyof T>(obj: T, names: K[]): Array<T[K]> { //返回值类型 Array<string | number> 
    return names.map((n) => obj[n])
}
const infoObj = {
    name: 'lison',
    age: 18,
}

let infoValues: Array<string | number> = getValue(infoObj, ['name', 'age'])

前缀修饰符: + -

映射类型里的readonly?属性修饰符现在可以使用+或-前缀,来表示修饰符是添加还是移除。

interface Info1 {
  age: number;
  name: string;
  sex: string;
}
// 把属性转换为只读属性 在属性前面添加readonly修饰符。
type ReadonlyType<T> = {
  // keyof T 获取T的所有属性 age | name | sex
  // in 类似于 for..in循环遍历这些属性
  readonly [P in keyof T]: T[P]
  
  // 等价于
  // + readonly [P in keyof T]: T[P]
  //有一个前缀 + 代表添加readonly修饰符。
}
type ReadonlyInfo1 = ReadonlyType<Info1>
// 现在 ReadonlyInfo1 的属性都是只读的。
// 下面是 ReadonlyInfo1 现在的类型结构 :↓
// type ReadonlyInfo1 = {
//   readonly age: number;
//   readonly name: string;
//   readonly sex: string;
// }



// 现在把readonly修饰符去除掉
type RemoveReadonlyInfo2<T> = {
   - readonly [P in keyof T]: T[P]
   // readonly 前面有一个 - 号。代表去除属性前面的 readonly 修饰符
}
type Info1WithoutReadonly = RemoveReadonlyInfo2<ReadonlyInfo1>
// 去除了readonly 
// 下面是 Info1WithoutReadonly 现在的类型结构 :↓
// type Info1WithoutReadonly = {
//   age: number;
//   name: string;
//   sex: string;
// }