likes
comments
collection
share

从零开始的 TypeScript 学习(五)—— 高级一点的类型

作者站长头像
站长
· 阅读数 37

字面量类型

在 TypeScript 中,用字面量表示的类型,被称为字面量类型

const one: 1 = 1;
const man: "male" = "male"
const arr: [1, 2, 3, 4] = [1, 2, 3, 4]

上面示例中,都是正确的字面量类型的赋值方式。当变量类型定义是字面量类型时,变量就只能使用值类型的字面量作为变量自己的值。赋值为其他类型值就会报错。

const one: 1 = 2;   // 报错
const arr: [1, 2] = [1, 2, 3]   // 报错

遇到const命令声明的变量,如果代码里面没有注明类型,就会推断该变量是字面量类型。

const a = 1;    // const a: 1

上面示例中,变量 aconst 关键字声明的,TypeScript 就会推断它的类型是数字字面量类型1

由于 const 声明的对象是可以修改的,所以这种推断不适用于对象中的属性和方法,而是根据正确的值去判断对应的类型。

const obj = {
    a: 100,
    y: "123",
}
/*
类型
const obj: {
    a: number;
    y: string;
}
*/

上面示例中,obj 对象中 a 被推断成 number 类型 ,y 的类型被推断成 string 类型;也可以手动给对象赋值一个对象字面量类型。

const obj: {age: number, name: string, isWorking: boolean} = {
    age: 25,
    name: "Peter",
    c: true
}

上面示例中,obj 获得了字面量类型 {age: number, name: string, isWorking: boolean}obj 中的属性值类型、属性个数、属性名都不可以随便添加和修改。初始化时也要符合对象字面量类型的声明规范,否则就会报错。


值类型可能会出现一些很奇怪的报错。

const x: 5 = 4 + 1;  // 报错 

上面示例中,等号左侧是数字字面量类型 5 ,等号右侧是 4 + 1 (加法运算的优先级比赋值更高)的类型值;TypeScript 会推断类型是 number 。我们可以使用类型断言收束等号右边的值的类型。

const x: 5 = (4 + 1) as 5
const y: 5 = <5>(4 + 1)

单一的字面量类型很少被使用,在开发中一般使用联合类型扩大字面量类型的用法。

联合类型

联合类型是使用竖线符号 | 分割多个类型,将多个类型组成的一个新类型。

let x: number | string
x = 1
x = "1"

上面示例中,x 既可以是 number类型又可以是 string 类型,它的值可以是字符串,也可以是数字。

联合类型可以与字面量类型结合,表示变量只能在若干个字面量类型中赋值,类似于一种枚举。

let ishot: true | false = true  // 相当于 boolean 类型
let gender: 'male' | 'female' = 'male'
let primaryColors: "red" | "green" | "blue" = 'red'

如果一些变量可能是空值 undefinednull 也可以使用联合类型进行限制;

let x: string | null = "123"
x = null

如果一个变量拥有多个类型,那么在使用某些类型特有方法时,可能会报错,所以需要使用类型收缩或类型断言去缩小变量的类型范围。

function printId(id: number | string) {
  console.log(id.toUpperCase()); // 报错
}

上面示例中,参数变量id可能是数值,也可能是字符串,这时直接对这个变量调用toUpperCase()方法会报错,因为这个方法只存在于字符串原型上,不存在于数值的原型中。

function func(id: number | string) {
    console.log((id as string).toUpperCase());  //  类型断言
    console.log((<string>id).toUpperCase());    //  类型断言
    if (typeof id === 'string') {               //  类型收缩
        console.log(id.toUpperCase());  
    }
}

上述示例中,缩小了 id 的范围,告诉 Typescript 是一种字符串类型,以便可以调用字符串原型上面的方法。

可以判断类型的条件语句都可以作为类型收缩的一种方法:

function getPort(scheme: "http" | "https") {
  switch (scheme) {
    case "http":
      return 80;        // http 的默认端口是 80 
    case "https":
      return 443;       // hhtps 的默认端口号是 443
  }
}

上面示例中,函数体内部对参数变量 scheme 进行类型缩小,根据不同的字面量类型,返回不同的结果。

交叉类型

如果联合类型使用的是"或"运算符,那么交叉类型必定就使用"与"运算符了吧~

交叉类型使用符号&表示,指变量同时拥有多种类型。

let x: string & number
x = 123;        // 报错
x = "123";      // 报错

这是一件非常荒谬的事情,x 变量的值不可能既是 string 类型,又是 number 类型。两种确定类型的交叉类型就像是 never 类型,永远不能取到值。在编辑器中,这种类型也确实会被推断成 never 类型。

在声明对象时,交叉类型可以起到很好的效果。

let person: { name: string } & { age: number };

obj = {
  name: "Rick",
  age: 70,
};

上面示例中,声明了一个 name 类型交叉 age 类型的变量吗,这与直接声明一个 {name: string, age: number} 没有区别。

Type

type 可以用来定义类型的别名,别名可以让类型的名字变得更有意义,也能增加代码的可读性,还可以使复杂类型用起来更方便,便于以后修改变量的类型。

通常类型别名的首字母要大写:

type Person = {name: string, age: number};
let person: Person = {
    name: "Rick",
    age: 70,
}

上面示例中,声明了一个名为 Person 的类型别名,又声明了一个对象,它的类型是 Person

别名定义之后只能当作类型使用,不能当作变量,声明之后不可以使用等号进行赋值操作:

type Point = {
    x: number;
    y: number;
}
Point = {};     // 报错

上面示例中,声明了一个名为 Point 的类型别名,当想更改 Point 的值时,Typescript 会提示报错,因为 Point 是类型,不能当作变量(值)去使用。但是类型可以当作一个类型去给另一个类型赋值:

type Person = {
    name: string;
    age: number
}

type Teacher = Person   // 不报错

类型别名的命名不允许重复:

type Color = "red";
type Color = "blue"; // 报错

类型别名属于块级作用域:

type Color = "red";
if (Math.random() < 0.5) {
  type Color = "blue";
}

可以使用交叉类型定义不同的对象类型:

type Point = {
    x: number,
    y: number
}
type Line = { x2: number, y2: number } & Point
const point: Point = { x: 1, y: 1 }
const line: Line = { x: 0, y: 0, x2: 2, y2: 3 }

别名也可以用作类型断言:

type Str = string

function func(id: number | string) {
    console.log((id as Str).toUpperCase());
    console.log((<Str>id).toUpperCase());
    if (typeof id === 'string') {
        console.log(id.toUpperCase());
    }
}

类型别名就好像一个 const 声明的变量,属于块级作用域,命名不能重复,声明之后不可以修改,但是可以给其他类型赋值。

类型别名只能当作类型使用,只能放在使用类型的地方,不能用作变量。

type 命令属于类型相关的代码,编译成 JavaScript 的时候,会被全部删除。

typeof

在 JavaScript 中,typof 运算符返回一个值的类型。

typeof x 会以字符串的形式返回数据类型:

console.log(typeof true)          // "boolean"
console.log(typeof 123)           // "number"
console.log(typeof "234")         // "string"
console.log(typeof undefined)     // "undefined"
console.log(typeof null)          // "object"
console.log(typeof [4, 5, 6])     // "object"
console.log(typeof Math)          // "object"  
console.log(typeof alert)         // "function"
console.log(typeof 10n)           // "bigint"
console.log(typeof Symbol("id"))  // "symbol"

TypeScript 也拥有 typeof 运算符,可以在类型上下文中使用它来引用变量或属性的类型,它的返回值是一个 TypeScript 类型:

const person = {
    name: 'Rick',
    age: 70
};
const student: typeof person = {
    name: "Morty",
    age: 14,
};
type Person = typeof person;
type Name = typeof person.name;

上面示例中,typeof persontypeof person.name 表示变量 person 的类型 { name: string; age: number; }person.name 的类型 string

在 TypeScript 的运算中,如果 typeof 运行在值运算中,大概率表示 JavaScript 中的代码,返回的就是类型字符串,如果运行在类型运算或赋值中,那么返回的就是 TypeScript 类型。

本章最后

上述内容如果有任何问题和错误,希望大佬们可以指出。

也不知道应该写长一点还是写短一点才能引起你们的注意,不过我一定会尽力坚持下去的~~~

转载自:https://juejin.cn/post/7381855349462794249
评论
请登录