类型声明

使用 :[type] 指定变量的类型

let a: number = 1 // 数字
let b: string = 'zhang' // 字符串
let c: boolean = true // 布尔
let d: object = {} // 对象
let e: array = [] // 数组
let f: null = null // Null
let g: undefined = undefined // Undefined
let h: void = function(){} // 空值
let i: any = [] // 表示任意类型,可以链式传播,不会对其进行类型检查
let j: unknown; // 表示不确定类型,只能赋值给unknown和any,会进行类型检测
let k: never; // 表示一个不可能出现的值,比如抛出错误,无限循环,并可以模拟只读属性
// 未声明类型的变量默认为any
let something === let something: any;

类型推论

如果没有指定类型,那么TS会按照类型推论推断出一个类型

let myFavoriteNumber = "seven";
myFavoriteNumber = 7; // error

联合类型

表示可以取多个类型的一种

let myFavoriteNumber: string | number;
myFavoriteNumber = "seven";
myFavoriteNumber = 7;
// 只能访问联合类型的共有属性(类型推断)
function getLength(something: string | number): number {
  return something.toString();
}

对象类型(interface)

我们使用接口(Interfaces)来定义对象的类型。

使用任意属性时,其他属性必须为任意属性的子集,可以使用联合类型

interface A {
  age: number, // 确定属性
  readonly name: boolean // 只读属性
  male?: number // 可选属性
  [propName: string]: string | number | boolean // 任意属性
}
let obj: A = {
  name: true,
  age: 12,
  zjang: '123',
}

数组类型

let fibonacci: number[] = [1, 1, 2, 3, 5]; // 类型 + []
let fibonacci1: Array<number> = [1, 1, 2, 3, 5]; // 数组泛型
interface NumberArray {
  [index: number]: number;
}
let fibonacci2: NumberArray = [1, 1, 2, 3, 5]; // 接口(类数组)

函数类型

函数声明

function sum(x: number = 123, y?: number): number {
  // 校验输入和输出
  return x + y;
}
sum(1, 2);
// 可设置默认值
// 可选参数后不允许再出现必须参数

函数表达式

let sum: (x: number, y: number) => number = function (
  x: number,
  y: number
): number {
  return x + y;
};
sum(1, 2);
// TS中=>表示的是函数定义,左边输入类型,右边输出类型

接口定义

interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (source: string, subString: string) {
  return source.search(subString) !== -1;
};

重载

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

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
  if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''));
  } else if (typeof x === 'string') {
    return x.split('').reverse().join('');
  }
}
// TS会优先从最前面的函数定义开始匹配,所以要把精确匹配放在前面

类型断言

可以手动指定一个值的类型:值 as 类型

将联合类型断言为其中一个类型

interface Cat {
  name: string;
  run(): void;
}
interface Fish {
  name: string;
  swim(): void;
}
function isFish(animal: Cat | Fish) {
  if (typeof (animal as Fish).swim === 'function') {
    return true;
  }
  return false;
}
// 需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误,比如执行了一个本不是它自己的方法。

将父类断言为更为具体的子类

interface ApiError extends Error {
  code: number;
}
interface HttpError extends Error {
  statusCode: number;
}
function isApiError(error: Error) {
  if (typeof (error as ApiError).code === 'number') {
    return true;
  }
  return false;
}

将任何一个类型断言为 any

window.foo = 1; // error
(window as any).foo = 1; // √

将 any 断言为一个具体的类型

function getCacheData(key: string): any {
  return (window as any).cache[key];
}
interface Cat {
  name: string;
  run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
// getCacheData可能是是一个第三方库,不确定他的返回类型,当我们在使用时

断言的限制-兼容

interface A {
  name: string;
}
interface B {
  name: string;
  run(): void;
}
let tom: B = {
  name: "Tom",
  run: () => {
    console.log("run");
  },
};
let animal: A = tom; // √
// 若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A
// 同理,若 B 兼容 A,那么 A 能够被断言为 B,B 也能被断言为 A

双重断言

interface Cat {
  run(): void;
}
interface Fish {
  swim(): void;
}
function testCat(cat: Cat) {
  return (cat as any as Fish);
}
let cat = {
  run() {
    console.log(1);
  }
}
testCat(cat).swim()
// 这是非常危险的,可能导致运行时错误

类实现接口implements

interface ClockInterface {
  currentTime: Date;
  setTime(d: Date);
}
class Clock implements ClockInterface { //
  currentTime: Date;
  setTime(d: Date) {
  this.currentTime = d;
}
constructor(h: number, m: number) {}
}

类修饰符static、public、private、protected

class Person {
  protected name: string // 行为类似private,但只可继承
  constructor(name: string){
    this.name = name
  }
}
class Zhang extends Person {
  constructor(name: string) {
    super(name)
  }
  static a = 'a' // 全局属性,创建全局变量
  public b = 'b' // 默认共有属性,内部外部都可访问
  private c = 'c' // 私有属性,外部不可访问
}
Zhang.a // a
new Zhang('zhang').b // b
new Zhang('zhang').c // error!
new Zhang('zhang').name // error!
new Person('zhang').name // error!

抽象类abstract

abstract class Person {
  private name: string // 行为类似private,但只可继承
  constructor(name: string){
    this.name = name
  }
  call(){
    console.log('call')
  }
  abstract show(): void
}
class Zhang extends Person {
  constructor(name: string) {
    super(name)
  }
  show(){
    console.log('show')
  }
  bind(){
    console.log('bind')
  }
}
let zhang: Person // 允许创建一个抽象类型的引用
zhang = new Person('zhang') // error! 不允许实例化抽象类
zhang = new Zhang('zhang') // 可以实例化派生类
zhang.call()
zhang.show()
zhang.bind() // error! 没有该方法

泛型

要求传入的类型和返回的类型一致

interface A {
  (arg: T): T
}
let fun: A
fun(1)
// 将泛型接口当做整体类型的一个接口
interface B<T> {
  (arg: T): T
}
let fun: B<number>
fun("string") // error! 直观的看到类型
fun(123) // √
// 泛型约束
interface A {
  length: number
}
function a<T extends A>(arg: T): T{
  console.log(arg.length)
  return arg
}
a(123) // error! 参数arg必须符合接口A的规范
a([1,2,3]) // √
❤️ 转载文章请注明出处,谢谢!❤️