TypeScript学习记录-一些方法、特性

96 阅读5分钟

类型方法/特性

自动类型判断(类型推论)

  • TS拥有自动的类型判断机制
  • 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
  • 所以如果变量的声明和赋值时同时进行的,可以省略掉类型声明

type(类型别名)

  • 为任意类型起别名
  • 使用 type 关键字来创建类型别名
  • 使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
type nihao = 1|2|3
let k:nihao;

interface(接口)

接口声明是命名对象类型的另一种方式

interface Point {
  x: number;
  y: number;
}
 
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

类型别名和接口的区别

类型别名和接口非常相似,在很多情况下你可以在它们之间自由选择。interface 的几乎所有功能都在 type 中可用,主要区别在于无法重新打开类型以添加新属性,而接口始终可扩展

  • type 不得参与在声明合并中,但 Interface 可以
interface Mammal {
    genus: string
}

interface Mammal {
    breed?: string
}

const animal: Mammal = {
    genus: "1234",
    breed: "123"
}

type Reptile = {
    genus: string
}

// 报错,不能与上面的 Reptile 合并
type Reptile = {
    breed?: string
}
  • Interface 只能用于声明对象的形状,而不是重命名基础类型
// 报错
interface X extends string {}
  • Interface 名称将在错误消息中显示为总是以原来的形式出现,但仅当它们被名称使用时
interface Mammal {
    name: string
}

function echoMammal(m: Mammal) {
    console.log(m.name)
}

// 报错,提示 name 必须为字符串
echoMammal({ name: 12343 })

泛型(Generic)

|(联合类型)

由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种

let arr: (number | string)[] = [1, 'a', 3, 'b']

代码缩小联合

如果有共同属性就不用缩小

function printId(id: number | string) {
  if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id);
  }
}

function welcomePeople(x: string[] | string) {
  if (Array.isArray(x)) {
    // Here: 'x' is 'string[]'
    console.log("Hello, " + x.join(" and "));
  } else {
    // Here: 'x' is 'string'
    console.log("Welcome lone traveler " + x);
  }
}

as/<>(类型断言)

有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:

// 第一种(常用)
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// 第二种
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;

// 类型断言,用来告诉解析器变量的实际类型
bb = aa as string;
// 或者
bb = <string>aa;

此规则可能过于保守,并且不允许可能有效的更复杂的强制转换。如果发生这种情况,可以使用两个断言,首先是 any(或 unknown,我们稍后会介绍),然后是所需的类型:

const a = expr as any as T;

&(同时)

同时

let j:{ name:string} & { age:number }
j = {name:'swk', age:18}

?(可选)

可选

// {} :指定对象中可以包含哪些属性
let bbb: {name: string,age?:number};
bbb = {name:'nihao',age:18};

!(非空断言)

该值不是 null 或 undefined

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

[propName: string]: any

任意类型的属性


// [propName: string]: any ----表示任意类型的属性
let ccc:{name:string,[xxx: string]: any};
ccc = {name:'dishitian', age:18,sex:'男'}

类型缩小

typeof

  • 使用 typeof 来进行类型判断,使用 typeof 会返回一组特定字符串
    • "string"
    • "number"
    • "bigint"
    • "boolean"
    • "symbol"
    • "undefined"
    • "object"
    • "function"
  • 真值缩小,以下的值转换成 boolean 会转换为 false
    • 0
    • NaN
    • ""(空字符串)
    • 0n(bigint 版本零)
    • null
    • undefined

相等性缩小

TypeScript 还使用 switch 语句和 ===、!==、== 和 != 等相等性检查来缩小类型

in 运算符缩小

type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
 
function move(animal: Fish | Bird | Human) {
  if ("swim" in animal) {
    animal; // (parameter) animal: Fish | Human
  } else {
    animal; // (parameter) animal: Bird | Human
  }
}

instanceof 缩小

x instanceof Foo 检查 x 的原型链是否包含 Foo.prototype

赋值

// let x: string | number
let x = Math.random() < 0.5 ? 10 : "hello world!";

// let x: number
x = 1;
console.log(x);

// let x: string
x = "goodbye!";
console.log(x);

使用类型谓词

更直接地控制类型在整个代码中的变化方式,只需要定义一个返回类型为类型谓词的方法:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}
  • pet is Fish 是本例中的类型谓词。谓词采用parameterName is Type的形式,其中 parameterName 必须是当前函数签名中的参数名称
  • 任何时候使用某个变量调用 isFish 时,如果基础类型兼容,TypeScript 就会将该变量缩小到该特定类型
let pet = getSmallPet();
 
if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

可以使用类型保护 isFish 过滤 Fish | Bird 的数组并获得 Fish 的数组:

const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()];
const underWater1: Fish[] = zoo.filter(isFish);
// 或者
const underWater2: Fish[] = zoo.filter(isFish) as Fish[];
 
// 更复杂的例子,可能需要重复谓词
const underWater3: Fish[] = zoo.filter((pet): pet is Fish => {
  if (pet.name === "sharkey") return false;
  return isFish(pet);
});

断言函数

判断联合

定义一个形状

interface Shape {
  kind: "circle" | "square";
  radius?: number;
  sideLength?: number;
}

再设计一个求面积的方法,但是两种图形求面积公式不同,所以需要在传进来的时候判断类型

function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    // 'shape.radius' is possibly 'undefined'.
    return Math.PI * shape.radius ** 2;
  }
}

// 使用非空断言强制表示 radius 存在
function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius! ** 2;
  }
}

但这感觉并不理想。这里不得不用那些非空断言(!)强制类型检查器确认它定义了 shape.radius,如果开始移动代码,这些断言很容易出错

这里重新定义区分

interface Circle {
  kind: "circle";
  radius: number;
}
 
interface Square {
  kind: "square";
  sideLength: number;
}
 
type Shape = Circle | Square;

再次尝试检查 kind 属性

function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
  }
}

never 类型

拿来兜底

穷举检查

type Shape = Circle | Square;
 
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

添加新成员,如果不给新成员指定,则会报错

interface Triangle {
  kind: "triangle";
  sideLength: number;
}
 
type Shape = Circle | Square | Triangle;
 
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
// Type 'Triangle' is not assignable to type 'never'.
      return _exhaustiveCheck;
  }
}