工具类型
TypeScript 提供了许多内置的工具类型,可以帮助开发者进行类型转换、元组操作、映射类型、条件类型等。以下是一些常用的 TypeScript 工具类型及其用法:
-
Partial<T>- 用途:将类型
T中的所有属性变为可选属性。 - 示例:
Partial<{ name: string; age: number; }>
- 用途:将类型
-
Required<T>- 用途:将类型
T中的所有可选属性变为必选属性。 - 示例:
Required<{ name?: string; age?: number; }>
- 用途:将类型
-
Readonly<T>- 用途:将类型
T中的所有属性变为只读属性。 - 示例:
Readonly<{ name: string; age: number; }>
- 用途:将类型
-
Record<K, T>- 用途:创建一个具有指定键类型
K和值类型T的对象类型。 - 示例:
Record<'a' | 'b', number>
- 用途:创建一个具有指定键类型
-
Pick<T, K>- 用途:从类型
T中选取指定的属性K组成新类型。 - 示例:
Pick<{ name: string; age: number; }, 'name'>
- 用途:从类型
-
Omit<T, K>- 用途:从类型
T中排除指定的属性K组成新类型。 - 示例:
Omit<{ name: string; age: number; }, 'age'>
- 用途:从类型
-
Exclude<T, U>- 用途:从类型
T中排除可以赋值给类型U的所有类型。 - 示例:
Exclude<'a' | 'b', 'a'>
- 用途:从类型
-
Extract<T, U>- 用途:从类型
T中提取可以赋值给类型U的所有类型。 - 示例:
Extract<'a' | 'b', 'a'>
- 用途:从类型
-
NonNullable<T>- 用途:从类型
T中排除null和undefined类型。 - 示例:
NonNullable<string | null | undefined>
- 用途:从类型
-
ReturnType<T>- 用途:获取函数类型
T的返回值类型。 - 示例:
ReturnType<() => string>
- 用途:获取函数类型
-
Parameters<T>- 用途:获取函数类型
T的参数类型组成的元组类型。 - 示例:
Parameters<(x: number, y: number) => number>
- 用途:获取函数类型
-
ConstructorParameters<T>- 用途:获取构造函数类型
T的参数类型组成的元组类型。 - 示例:
ConstructorParameters<typeof Date>
- 用途:获取构造函数类型
-
InstanceType<T>- 用途:获取构造函数类型
T的实例类型。 - 示例:
InstanceType<typeof Date>
- 用途:获取构造函数类型
-
ThisType<T>- 用途:用于指定函数中
this的类型。 - 示例:
function sayHello(this: { name: string }) { console.log('Hello, ' + this.name); }
- 用途:用于指定函数中
这些工具类型可以帮助开发者更方便地进行类型操作和转换,提高了 TypeScript 代码的可读性和维护性。
类的高级用法
typeof 类 与 new()=>类
-
typeof 类 获取类的构造函数类型,包括构造函数签名和静态成员。这个类型包含了类的实例属性、静态属性、方法等信息。
-
new () => 类 是一个函数类型,明确表示一个构造函数签名,用于描述可以用 new 关键字调用的构造函数。
两者在定义和使用上有所不同:
-
typeof 类 更适合用于获取一个类的构造函数类型,用于类型检查和创建该类实例的场合。
-
new () => 类 更适合用于描述和约束构造函数类型,通常用于依赖注入或者工厂函数中,以确保传入的构造函数符合预期的签名。
class BeeKeeper {
hasMask: boolean = true;
}
class ZooKeeper {
nametag: string = "ZooKeeper";
}
class Animal {
numLegs: number = 4;
}
class Bee extends Animal {
keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
keeper: ZooKeeper = new ZooKeeper();
}
// 使用 new () => 类
function createInstance<A extends Animal>(c: new () => A): A {
//console.log(c.type); // 访问静态变量,typescript编译器报错
return new c();
}
const lion = createInstance(Lion);
console.log(lion.keeper.nametag); // 输出 'ZooKeeper'
const bee = createInstance(Bee);
console.log(bee.keeper.hasMask); // 输出 'true'
// 使用typeof 类,可以访问静态变量
class Animal {
numLegs: number = 4;
static type: string = "Animal";
}
class Bee extends Animal {
keeper: string = "BeeKeeper";
static type: string = "Bee";
}
class Lion extends Animal {
keeper: string = "ZooKeeper";
static type: string = "Lion";
}
function createInstance<T extends typeof Animal>(c: T): InstanceType<T> {
console.log(c.type); // 访问静态变量
return new c() as InstanceType<T>;
}
const lion = createInstance(Lion);
const bee = createInstance(Bee);
类型兼容性
TypeScript 的类型兼容性主要是指当一种类型的变量可以赋值给另一种类型的变量时,它们之间的兼容关系。
TypeScript 使用结构类型系统(structural type system),即基于对象的形状(结构)而非名义(名称)来判断类型的兼容性。
类型兼容性规则
-
结构兼容性:如果一个类型 Y 中的所有成员在类型 X 中都存在,那么类型 Y 就是兼容于类型 X 的。简而言之,类型 Y 必须至少有类型 X 中所有的属性和方法。
-
函数兼容性:对于函数类型的兼容性,参数类型要完全匹配或包含在目标类型中,而返回类型必须完全匹配或兼容于目标类型。
-
基本类型兼容性:基本类型(如 number、string、boolean)直接判断是否为相同类型。
示例
基本类型的兼容性
let num: number = 42;
let str: string = "hello";
num = 100; // OK
str = "world"; // OK
// num = str; // Error: Type 'string' is not assignable to type 'number'.
对象类型的兼容性
interface Person {
name: string;
age: number;
}
interface Employee {
name: string;
age: number;
salary: number;
}
let person: Person = { name: "Alice", age: 25 };
let employee: Employee = { name: "Bob", age: 30, salary: 50000 };
person = employee; // OK: Employee has at least the same properties as Person
// employee = person; // Error: Property 'salary' is missing in type 'Person'
函数类型的兼容性
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK: x's parameters (number) are a subset of y's parameters (number, string)
// x = y; // Error: x's parameters are not a superset of y's parameters
let z = (a: number) => "hello";
x = z; // OK: return type 'string' is assignable to return type 'number'
泛型类型的兼容性
泛型类型的兼容性基于其在使用时的具体类型参数。
interface Container<T> {
value: T;
}
let numberContainer: Container<number> = { value: 42 };
let stringContainer: Container<string> = { value: "hello" };
// numberContainer = stringContainer; // Error: Type 'Container<string>' is not assignable to type 'Container<number>'
类的兼容性
类的兼容性类似于接口,但需要注意的是,类的私有成员和受保护成员会影响兼容性。
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
let animal: Animal;
let dog: Dog;
animal = dog; // OK
// dog = animal; // Error: Property 'breed' is missing in type 'Animal'
总结
TypeScript 的类型兼容性是基于结构的,这意味着如果一个类型至少具有另一个类型的所有属性和方法,那么这两个类型就是兼容的。对于函数类型和类的兼容性,TypeScript 也遵循类似的规则,但会考虑函数的参数和返回类型,以及类的私有和受保护成员。这种基于结构的类型系统使得 TypeScript 具有很强的灵活性和实用性。