TS常见问题汇总

235 阅读4分钟

“这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

TS中的强制类型转换

先看一个例子:

type T = {
  title: string
}
type U = {
  color: string
}
let result = <T&U>{}

result.title = '北京'
result.color = 'red'

如果我们写了一个空对象,需要给这个对象添加属性,这个时候是会报错的:

let result = {}
// 报错:提示不存的属性
result.title = '北京'

这个时候我们需要进行强制类型转换:

type T = {
  title: string
}
let result = <T>{}
// 不会报错了
result.title = '北京'

你也可以进行类型断言

type T = {
  title: string
}
let result = {};

(result as T).title = '北京'

注意写法有很多种

type T = {
  title: string
}
type U = {
  color: string
}
let result = {} as T;

result.title = '北京'

交叉类型

上面示例中使用到了交叉类型

interface A {
  name: string;
  sex: number;
}

interface B {
  age: number;
  sex: number;
}

type C = A&B
let c:C = { name: 'xxx', age: 18, sex: 1 }

既是A的子类型,又是B的子类型,既要满足A又要满足B

type AA = string | number;
type BB = string | boolean;
type CC = AA & BB;

那么CC是string类型

TS中的unknown类型

我们可以对 any 进行任何操作,不需要检查类型。

let value:any;
value = ture;
value = 1;
value.length;

没有类型检查就没有意义了,跟写JS一样。很不安全。

也可以把任何值赋值给 unknown

let value:any;
value = ture;
value = 1;

但是不能调用属性和方法

value.length; // 错误写法

如果需要调用属性和方法,那么你可能需要类型断言

let value:unknown;
value = 'hello';
(value as string).length

再或者使用类型保护

let value:unknown;
value = 'hello';
if (typeof value === 'string') {
  value.length
}

联合类型中的 unknown定义

如果联合类型中有unknown,那么最终得到的都是unknown类型

type U1 = unknown | null;
type U2 = unknown | string;
type U3 = unknown | number;

类型别名 U1,U2,U3 都是 unknown 类型

ts的类型兼容性

你要的我有就可以,没有就不行

1、接口兼容性

interface Person1 {
  name: string
}
interface Person2 {
  name: string;
  age: number;
}

let obj1: Person1 = {
  name: 'xxx'
}

let obj2: Person2 = {
  name: 'xxx',
  age: 18
}

obj2 = obj1 // 错误:无法赋值,因为缺少属性,多了不影响,但是少了属性绝对不行

obj1 = obj2 是可以的

2、基本类型的兼容性

let num1: number
let num2: number | string

num1 = 10;
num2 = 'xxx'

num1 = num2 // 错误:无法赋值,因为缺少类型,多了不影响,但是少了类型绝对不行

num2 = num1 是可以的

3、类的兼容性

class Animal {
  name: string
}
class Bird extends Animal {
  age: number
}

let a: Animal;
let b = Bird;

b = a  // 错误:无法赋值,因为缺少类型,多了不影响,但是少了类型绝对不行

a = b 是可以的

4、函数的兼容性

type Func = (a: number, b: number) => number;

let sum: Func;
function f1 (a: number, b: number):void {
 // todo
}

sum = f1 // 错误:无法赋值,因为返回值类型不同

除了对比参数,还要对比返回值

type Func = (a: number, b: number) => number;

let sum: Func;
function f1 (a: number, b: number, c: number):number {
  return a
}

sum = f1 // 错误:无法赋值,多了参数不行,但是少参数可以
type GetPerson = () => {name: string, age: number};
let getPerson :GetPerson;
function g1 () {
  return {name: 'xxx'}
}
function g2 () {
  return {name: 'xxx', age: 18}
}
function g3 () {
  return {name: 'xxx', age: 18, sex: 1}
}

// getPerson = g1; 报错
// getPerson = g2; 正常
// getPerson = g3 正常

参数可以传自己和自己的父类,返回值可以传自己和自己的子类

条件类型约束

泛型约束的例子:

type MessageOf<T extends { message: unknown }> = T["message"];

在此示例中,我们使用 message: unknown 约束泛型T。

如果我们想 MessageOf 支持任何类型,我们可以通过将约束和条件类型一起使用:

type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;

如果条件成立,在 true 分支内,TypeScript 知道 T 将具有一个 message 属性。否则将会返回 never 类型。

typeof

获取一个值的类型

type T = {
  title: string
}
let person:T = {
  title: 'hello'
}

let user = {
  title: 'js'
}
// typeof 表示获取一个值的类型
type U = typeof user;

ReturnType

获取函数返回值的类型

function getUser() {
  return {name: 'xxx', age: 10}
}

type GetUserType = typeof getUser;
type ReturnUser = ReturnType<GetUserType>

获取函数参数的类型

function getUser() {
  return {name: 'xxx', age: 10}
}

type GetUserType = typeof getUser;
type ReturnUser = Parameters<GetUserType>

泛型约束

我们定义一个接口来描述约束条件。创建一个包含 .length 属性的接口,使用这个接口和 extends 关键字来实现约束:

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}