TS入门:高级类型
2024年7月24日
交叉类型
交叉类型表示一个值可以是几种类型的集合。我们用(&
)分割每个类型,所以 A & B
表示一个值可以是 A
和 B
的合集。
interface A {
name: string;
}
interface B {
age: number;
}
const c: A & B = {name: 'yux', age: 33};
联合类型
联合类型表示一个值可以是几种类型之一。 我们用竖线(|
)分隔每个类型,所以 number | string | boolean
表示一个值可以是 number
, string
,或 boolean
。
function a(x: string | number) {
if (typeof x === 'string') {
return `hello ${x}`;
}
if (typeof x === 'number') {
return x + 1;
}
}
类型保护
在我们使用联合类型时,有时候某个类型的方法在另一个类型上不存在,所在当我们需要调用某个方法时,就需要判断当前参数属于哪个类型,然后去调用对应的方法,但是在ts
检查时,这种判断会报错,看下面的例子:
interface A {
name: string;
setName(): void;
}
interface B {
age: number;
setAge(): void;
}
function a(x: A | B) {
if (x.setName) { // 报错,因为调用时,x可能是B类型,所以B.setName 不存在
x.setName();
} else if (x.setAge) {// 报错,因为调用时,x可能是A类型,所以A.setAge 不存在
x.setAge();
}
}
为了让已上代码能够正确运行,就需要用到类型保护了。
使用类型断言
function a(x: A | B) {
if ((<A>x).setName) { // OK
(<A>x).setName();
} else if ((<B>x).setAge) {// OK
(<B>x).setAge();
}
}
使用类型谓词
function isA(p: any): p is A {
return (<A>p).xx !== undefined;
}
p is A
就是类型谓词。 谓词为 parameterName is Type
这种形式, parameterName
必须是来自于当前函数签名里的一个参数名。
实际用例:
interface A {
name: string;
setName(): void;
}
interface B {
age: number;
setAge(): void;
}
function isA(p: any): p is A {
return (<A>p).setName !== undefined;
}
function a(x: A | B) {
if (isA(x)) {
x.setName();
} else {
x.setAge();
}
}
typeof
类型保护
function a(x: string | number) {
if (typeof x === 'string') {
return `hello ${x}`;
}
if (typeof x === 'number') {
return x + 1;
}
}
TIP
typeof
类型保护*只有两种形式能被识别: typeof v === "typename"
和 typeof v !== "typename"
, typename
必须是 number
, string
, boolean
或 symbol
。 但是TypeScript
并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。
instanceof
类型保护
class A {
constructor() { };
getName() {
return 'yux';
};
}
class B {
constructor() { };
getAge() {
return 33;
};
}
function get(p: A | B): string | number | void {
if (p instanceof A) {
return p.getName();
}
if (p instanceof B) {
return p.getAge();
}
}
get(new A)
get(new B)
使用 !
去除 null
// 错误的情况
function a(x: string | null): string {
if (x.length > 0) { // Error, x 有可能是null
return `${x} is string`;
}
return `${x} is null`;
}
// 使用 `!` 来去除null的可能
function a(x: string | null): string {
if (x!.length > 0) { // OK
return `${x} is string`;
}
return `${x} is null`;
}
类型别名
类型别名使用 type
来定义,它会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。
type s = string;
type n = number;
type b = boolean;
// type可以是联合类型
type e = s | n | b;
let x: e = true;
x = 2;
x = 'xxx';
// type可以是泛型
type Container<T> = { value: T };
// type可以是交叉类型
type s = A & B;
// 可以是字符串字面量
type E = 'up' | 'down' | 'middle';
// 可以是数字字面量
type N = 1 | 2 | 3;
TIP
类型别名不能出现在声明右侧的任何地方。
字符串字面量类型
TS
允许你定义字符串字面量类型,你只能从定义的的字符中选择其一来做为值,传入其它值则会产生错误。
type s = 'up' | 'down' | 'middle';
const a: s = 'up'; // OK
const b: s = 'xx'; // Error
数字字面量类型
TS
允许你定义数字字面量类型,你只能从定义的的数字中选择其一来做为值,传入其它值则会产生错误。
type n = 1 | 2 | 3;
const a: n = 1; // OK
const b: n = 10 // Error;
索引类型
首先是使用 keyof
作为索引类型查询操作符,keyof T
的结果为 T上已知的公共属性名的联合。第二个操作符是 T[K]
, 索引访问操作符。 在这里,类型语法反映了表达式语法。 这意味着 person['name']
interface Person {
name: string;
age: number;
}
// keyof Person 表示a的类型是Person类型key的集合
const a: keyof Person = 'name';
// K作为属性名集合,T[K]表示索引访问,即对象obj[key]
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const p: Person = {
name: 'yux',
age: 33,
}
const v: string = getValue(p, 'name');
映射类型
TS
提供了从旧类型中创建新类型的一种方式 — 映射类型。
最简单的映射类型,它的语法与索引签名的语法类型,内部使用了 for .. in。 具有三个部分:
- 类型变量 K,它会依次绑定到每个属性。
- 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
- 属性的结果类型。
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
const f: Flags = {
option1: true,
option2: false,
}
TS
内置的映射类型工具
// 将类型 `T` 的所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
// 将类型 `T` 的所有属性变为可选。
type Partial<T> = {
[P in keyof T]?: T[P];
}
// 从一个类型中选择一组属性,构造一个新的类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
// 构造一个对象类型,其键类型为 `K`,值类型为 `T`。
type Record<K extends keyof any, T> = {
[P in K]: T;
};
示例:
interface User {
id: string;
name: string;
age: number;
}
// Readonly
const user: Readonly<User> = {
id: '1',
name: 'yux',
age: 11,
}
user.id = '2'; // Error, Cannot assign to 'id' because it is a constant or a read-only property.
// Partial
const user2: Partial<User> = {
name: 'yux',
age: 33,
} // OK, 使用Partial将属性变为可选,所以id、name、age都可以没有
// Pick,从User中,选择name、age创建新类型
const user3: Pick<User, 'name' | 'age'> = {
name: 'yux',
age: 33,
}
// Record,使用Record,创建key为string,值为User类型的数据
const users: Record<string, User> = {
"user1": { id: "user1", name: "Alice", age: 25 },
"user2": { id: "user2", name: "Bob", age: 30 },
};
console.log(users["user1"].name); // 输出: Alice
由映射类型进行推断
预定义的有条件类型
TypeScript 2.8
在lib.d.ts
里增加了一些预定义的有条件类型:
Exclude<T, U>
-- 从T中剔除可以赋值给U的类型。Extract<T, U>
-- 提取T中可以赋值给U的类型。NonNullable<T>
-- 从T中剔除null和undefined。ReturnType<T>
-- 获取函数返回值类型。InstanceType<T>
-- 获取构造函数类型的实例类型。
// 从"a" | "b" | "c" | "d"中剔除"a" | "c" | "f"
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
// 从"a" | "b" | "c" | "d"提取"a" | "c" | "f",但是只有"a" | "c"重合
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
// 从<string | number | (() => void)中剔除Function
type T02 = Exclude<string | number | (() => void), Function>; // string | number
// 从<string | number | (() => void)中提取Function
type T03 = Extract<string | number | (() => void), Function>; // () => void
// 从string | number | undefined中删除null和undefined
type T04 = NonNullable<string | number | undefined>; // string | number
// 从(() => string) | string[] | null | undefined中删除null和undefined
type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
function f1(s: string) {
return { a: 1, b: s };
}
class C {
x = 0;
y = 0;
}
// 获取函数返回值类型
type T10 = ReturnType<() => string>; // string
type T11 = ReturnType<(s: string) => void>; // void
type T12 = ReturnType<(<T>() => T)>; // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
type T14 = ReturnType<typeof f1>; // { a: number, b: string }
type T15 = ReturnType<any>; // any
type T16 = ReturnType<never>; // any
type T17 = ReturnType<string>; // Error
type T18 = ReturnType<Function>; // Error
// 获取构造函数类型的实例类型
type T20 = InstanceType<typeof C>; // C
type T21 = InstanceType<any>; // any
type T22 = InstanceType<never>; // any
type T23 = InstanceType<string>; // Error
type T24 = InstanceType<Function>; // Error
转载说明
本文允许全文转载,转载请注明来源: 平凡公子 - TS入门:高级类型