概述
复杂基础类型
数组:
ts的数组和元组转译为js后都是数组。
1.数组类型(Array)
- ts中可以像js一样定义数组类型,并且指定数组元素的类型。
- 定义:使用[]定义、使用Array泛型。但不推荐泛型:避免与jsx语法冲突,减少代码量。
- 明确指定数组元素的类型,不符合类型约定都会报错
/** 子元素是数字类型的数组 */
let arrayOfNumber: number[] = [1, 2, 3];
let arrayOfNumber: Array<number> = [1, 2, 3];
/** 子元素是字符串类型的数组 */
let arrayOfString: string[] = ['x', 'y', 'z'];
let arrayOfString: Array<string> = ['x', 'y', 'z'];
/** 定义好后不符合类型约定都会报错 */
let arrayOfNumber: number[] = ['x', 'y', 'z']; // 提示 ts(2322)
arrayOfNumber[3] = 'a'; // 提示 ts(2322)
arrayOfNumber.push('b'); // 提示 ts(2345)
let arrayOfString: string[] = [1, 2, 3]; // 提示 ts(2322)
arrayOfString[3] = 1; // 提示 ts(2322)
arrayOfString.push(2); // 提示 ts(2345)
2.元组类型(Tuple)
- 最重要的特性是可以限制数组元素的个数和类型,特别适合用来实现多值返回。
- 数组类型的值只有显示添加了元组类型注解后(或者使用 as const,声明为只读元组),ts才会把它当作元组,否则推荐出来的类型就是普通的数组类型
const x: [State, SetState] = [state, setState];
const y: [SetState, State] = [setState, state];
特殊类型:any、
any:任意类型
1.官方提供的一个选择性绕过静态类型检测的作弊方式。
2.可以对any类型的变量进行任何操作:获取事实上并不存在的属性、方法,并且ts还无法检测其属性是否存在、类型是否正确。
3.可以把任何类型的值赋值给 any 类型的变量,也可以把 any 类型的值赋值给任意类型(除 never 以外)的变量
4.any 类型会在对象的调用链中进行传导,即所有 any 类型的任意属性的类型都是 any
5.尽量避免使用
let anything: any = {};
anything.doAnything(); // 不会提示错误
anything = 1; // 不会提示错误
anything = 'x'; // 不会提示错误
let num: number = anything; // 不会提示错误
let str: string = anything; // 不会提示错误
// 类型传导
let anything: any = {};
let z = anything.x.y.z; // z 类型是 any,不会提示错误
z(); // 不会提示错误
unknown: 3.0 中添加的一个类型,主要用来描述类型并不确定的变量。
1.比如在多个 if else 条件分支场景下,它可以用来接收不同条件下类型各异的返回值的临时变量。
let result: unknown;
if (x) {
result = x();
} else if (y) {
result = y();
} ...
2.在类型上比any更安全。可以将任意类型的值赋值给 unknown,但 unknown 类型的值只能赋值给 unknown 或 any。
let result: unknown;
let num: number = result; // 提示 ts(2322)
let anything: any = result; // 不会提示错误
3.使用 unknown 后,ts会对它做类型检测。但是如果不缩小类型,对 unknown 执行的任何操作都会出现如下所示错误:
let result: unknown;
if (typeof result === 'number') {
result.toFixed(); // 此处 hover result 提示类型是 number,不会提示错误
}
void、undefined、null:实际上并没有太大的用处
- void :仅适用于表示没有返回值的函数。即如果该函数没有返回值,那它的类型就是 void。
在 strict 模式下,声明一个 void 类型的变量几乎没有任何实际用处。 - undefined 和 null : ts 值与类型关键字同名的唯二例外。但仍旧很废柴,因为单纯声明 undefined 或者 null 类型的变量也是无比鸡肋。
- undefined 的最大价值主要体现在接口类型上,表示一个可缺省、未定义的属性。
分享一个稍微有点费解的设计:可以把 undefined 值或类型是 undefined 的变量赋值给 void 类型变量,反过来,类型是 void 但值是 undefined 的变量不能赋值给 undefined 类型。
const userInfo: {
id?: number;
} = {};
let undeclared: undefined = undefined;
let unusable: void = undefined;
unusable = undeclared; // ok
undeclared = unusable; // ts(2322)
- null 的价值主要体现在接口制定上,表明对象或属性可能是空值。尤其是在前后端交互的接口,任何涉及查询的属性、对象都可能是 null 空对象。
const userInfo: {
name: null | string
} = { name: null };
- 除此之外,undefined 和 null 类型还具备警示意义,可以提醒我们针对可能操作这两种(类型)值的情况做容错处理。我们需要类型守卫在操作之前判断值的类型是否支持当前的操作。类型守卫既能通过类型缩小影响 ts 的类型检测,也能保障 js 运行时的安全性。
const userInfo: {
id?: number;
name?: null | string
} = { id: 1, name: 'Captain' };
if (userInfo.id !== undefined) { // Type Guard
userInfo.id.toFixed(); // id 的类型缩小成 number
}
- 不建议随意使用非空断言来排除值可能为 null 或 undefined 的情况,因为不安全。比非空断言更安全、类型守卫更方便的做法是使用单问号、双问号(空值合并)
userInfo.id!.toFixed(); // ok,但不建议
userInfo.name!.toLowerCase() // ok,但不建议
userInfo.id?.toFixed(); // Optional Chain
const myName = userInfo.name?? `my name is ${info.name}`; // 空值合并
never:永远不会发生值的类型
1.实际的场景:抛出错误的函数、死循环函数
// 因为永远不会有返回值,所以它的返回值类型就是 never
function ThrowError(msg: string): never {
throw Error(msg);
}
// 死循环,那么这个函数的返回值类型也是 never
function InfiniteLoop(): never {
while (true) {}
}
2.never 是所有类型的子类型,它可以给所有类型赋值,但除了 never 自身以外,其他类型都不能为 never 类型赋值。
let Unreachable: never = 1; // ts(2322)
Unreachable = 'string'; // ts(2322)
Unreachable = true; // ts(2322)
let num: number = Unreachable; // ok
let str: string = Unreachable; // ok
let bool: boolean = Unreachable; // ok
3.在恒为 false 的类型守卫条件判断下,变量的类型将缩小为 never。因此条件判断中的相关操作始终会报无法更正的错误(我们可以把这理解为一种基于静态类型检测的 Dead Code 检测机制)
const str: string = 'string';
if (typeof str === 'number') {
str.toLowerCase(); // Property 'toLowerCase' does not exist on type 'never'.ts(2339)
}
4.基于 never 的特性,还可以使用 never 实现一些有意思的功能。比如我们可以把 never 作为接口类型下的属性类型,用来禁止写接口下特定的属性
// 无论给props.name 赋什么类型的值,它都会提示类型错误,实际效果等同于 name 只读
const props: {
id: number,
name?: never
} = {
id: 1
}
props.name = null; // ts(2322))
props.name = 'str'; // ts(2322)
props.name = 1; // ts(2322)
object
非原始类型的类型,即非 number、string、boolean、bigint、symbol、null、undefined 的类型。但作用不大,如下所示的一个应用场景是用来表示 Object.create 的类型。
declare function create(o: object | null): any;
create({}); // ok
create(() => null); // ok
create(2); // ts(2345)
create('string'); // ts(2345)
类型断言
ts 类型检测无法做到绝对智能,有时会碰到我们比 ts 更清楚实际类型的情况,比如:
const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = arrayNumber.find(num => num > 2); // 提示 ts(2322)
/*
在 ts 看来,greaterThan2 的类型既可能是数字,也可能是 undefined,此时不能把类型
undefined分配给类型 number。但我们知道greaterThan2 一定是一个数字(确切地讲是 3),
因为 arrayNumber 中明显有大于 2 的成员,但静态类型对运行时的逻辑无能为力。
*/
所以可以使用类型断言(类似仅作用在类型层面的强制类型转换)告诉 ts 按照我们的方式做类型检查。1.类型断言的操作对象必须满足某些约束关系,否则我们将得到一个 ts(2352) 错误,即从类型“源类型”到类型“目标类型”的转换是错误的,因为这两种类型不能充分重叠。
2.any 和 unknown 这两个特殊类型属于万金油,因为它们既可以被断言成任何类型,反过来任何类型也都可以被断言成 any 或 unknown。
- 使用 as 语法做类型断言(推荐)
- 使用尖括号 + 类型的格式做类型断言
- 常量断言:字面量值 + as const
const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = arrayNumber.find(num => num > 2) as number;
const greaterThan2: number = <number>arrayNumber.find(num => num > 2);
// 常量断言
/** str 类型是 '"str"' */
let str = 'str' as const;
/** readOnlyArr 类型是 'readonly [0, 1]' */
const readOnlyArr = [0, 1] as const;
非空断言
- 在值(变量、属性)的后边添加 ‘!’ 断言操作符,排除值为 null、undefined 的情况。
- 非空断言是和 any 一样危险的选择。建议使用类型守卫来代替。
// 非空断言
let mayNullOrUndefinedOrString: null | undefined | string;
mayNullOrUndefinedOrString!.toString(); // ok
mayNullOrUndefinedOrString.toString(); // ts(2531)
// 类型守卫
if (typeof mayNullOrUndefinedOrString === 'string') {
mayNullOrUndefinedOrString.toString(); // ok
}
最后
以上就是个性机器猫为你收集整理的3.TypeScript复杂基础类型的全部内容,希望文章能够帮你解决3.TypeScript复杂基础类型所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复