- 泛型
- 1. 泛型约束(Generic Constraints)
- 2. 类型推断(Type Inference with Generics)
- 3. 多参数泛型(Multi-Parameter Generics)
- 4. 泛型接口(Generic Interfaces)
- 5. 泛型类(Generic Classes)
- 联合
- 1. 类型保护与类型断言(Type Guards)
- 2. 非空断言操作符(Non-null assertion operator, !)
- 3. 类型拆解(Distributive Type Operator, & 和 |)
- 4. 模式匹配(Pattern Matching)
- 交叉类型
- 结语
TypeScript 是一种由微软开发的编程语言,它是 JavaScript 的超集,为 JavaScript 添加了静态类型检查。在 TypeScript 中,泛型、联合类型和交叉类型是强大的高级类型技巧,可以帮助开发者更好地抽象和组织代码。
泛型
在 TypeScript 中,泛型是一种强大的工具,它允许我们编写可重用的组件,这些组件可以适应多种类型。
1. 泛型约束(Generic Constraints)
泛型可以被约束在一个特定的类型或类型接口上,确保传递给泛型的类型满足一定的条件。例如,如果我们希望一个函数只接收具有 length
属性的类型,我们可以这样做:
interface Lengthwise { length: number; } function printLength<T extends Lengthwise>(item: T): void { console.log(item.length); } const stringLength = "hello"; printLength(stringLength); // OK, strings have a length property const objectWithoutLength = { name: "World" }; printLength(objectWithoutLength); // Error, no length property
<T extends Lengthwise>
确保 T
必须具有 length
属性。
2. 类型推断(Type Inference with Generics)
TypeScript 允许在某些情况下自动推断泛型的类型,特别是在函数调用时。例如,使用 infer
关键字可以从函数类型中提取返回值类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never; function identity<T>(arg: T): T { return arg; } type IdentityReturnType = ReturnType<typeof identity>; // IdentityReturnType is inferred as 'string'
在这里,ReturnType
会从函数类型中提取返回值类型。
3. 多参数泛型(Multi-Parameter Generics)
可以定义接受多个泛型参数的类型或函数:
interface Pair<T, U> { first: T; second: U; } function createPair<T, U>(first: T, second: U): Pair<T, U> { return { first, second }; } const pair = createPair("Hello", 42); // pair has type Pair<string, number>
createPair
函数接受两个泛型参数 T
和 U
,并返回一个 Pair<T, U>
类型的对象。
4. 泛型接口(Generic Interfaces)
接口也可以使用泛型:
interface GenericIdentityFn<T> { (arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn<number> = identity; // myIdentity is a number-specific version of the identity function
这里,GenericIdentityFn
是一个泛型接口,identity
函数被赋值给它的一个实例,限制了它只能处理 number
类型的参数。
5. 泛型类(Generic Classes)
类也可以定义为泛型,允许类的方法和属性使用不同的类型:
class Box<T> { value: T; constructor(value: T) { this.value = value; } setValue(newValue: T): void { this.value = newValue; } } const boxOfStrings = new Box<string>("Hello"); boxOfStrings.setValue("World"); // OK boxOfStrings.setValue(123); // Error, incompatible types
Box
类接受一个泛型类型 T
,并在实例化时指定具体类型。
联合
TypeScript 中的联合类型(Union Types)允许我们将多种类型合并成一个新的类型,这意味着一个变量可以是多种类型中的一种。
1. 类型保护与类型断言(Type Guards)
当你处理联合类型时,有时需要确定变量的具体类型。这可以通过类型保护来实现,即编写一个函数或表达式来检查变量的性质,从而缩小其可能的类型范围。
例如:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number }; function getArea(shape: Shape): number { if ('radius' in shape) { // 类型保护:现在我们知道 shape 是 { kind: 'circle'; radius: number } return Math.PI * shape.radius ** 2; } else { // 类型保护:现在我们知道 shape 是 { kind: 'square'; side: number } return shape.side ** 2; } }
在这个例子中,通过检查 shape
是否有 radius
属性,我们能确定它是一个圆还是一个正方形。
2. 非空断言操作符(Non-null assertion operator, !)
非空断言操作符 !
可以用来告诉编译器,即使联合类型中可能包含 null
或 undefined
,你确定这个值不会是 null 或 undefined
。但是,如果判断错误,运行时可能会抛出错误:
function logValue(value: string | null | undefined): void { if (value) { console.log(value!.toUpperCase()); // 使用 ! 进行非空断言 } else { console.log('Value is null or undefined'); } }
在这里,如果 value
不是 null
或 undefined
,我们使用 !
来忽略潜在的 null
或 undefined
类型警告。
3. 类型拆解(Distributive Type Operator, & 和 |)
当你对一个泛型类型应用联合或交叉类型操作时,它们会“拆解”到泛型的每一个实例上。例如,对于一个数组的元素类型是联合类型的情况:
type NumbersOrStrings = number | string; type ArrayWithMixedElements<T> = T[]; const mixedArray: ArrayWithMixedElements<NumbersOrStrings> = [1, "two", 3];
mixedArray
的元素类型是 NumbersOrStrings
的每一个实例,所以它可以包含 number
或 string
。
4. 模式匹配(Pattern Matching)
在解构赋值、函数参数或类型别名中,可以使用模式匹配来处理联合类型的值:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number }; function handleShape(shape: Shape) { switch (shape.kind) { case 'circle': const { radius } = shape; // 现在我们知道了 shape 是 { kind: 'circle'; radius: number } break; case 'square': const { side } = shape; // 现在我们知道了 shape 是 { kind: 'square'; side: number } break; } }
在这个例子中,switch
语句作为类型保护,根据 kind
属性来处理不同类型的形状。
交叉类型
交叉类型(Intersection Types)在 TypeScript 中允许你将多个类型合并成一个新类型,这个新类型包含了所有原始类型的属性和方法。
1. 类型合并(Combining Types)
交叉类型使用 &
运算符来合并两个或更多类型。例如,假设我们有两个接口 Person
和 Employee
,我们可以创建一个 PersonAndEmployee
交叉类型:
interface Person { name: string; age: number; } interface Employee { id: number; department: string; } type PersonAndEmployee = Person & Employee; const person: PersonAndEmployee = { name: 'Alice', age: 30, id: 123, department: 'HR', };
person
变量现在必须同时符合 Person
和 Employee
接口的要求。
2. 类与接口的交叉
交叉类型也可以应用于类与接口之间,将类的实例与接口的属性和方法相结合:
class Animal { name: string; makeSound(): void { console.log('Making sound...'); } } interface HasColor { color: string; } class ColoredAnimal extends Animal implements HasColor { color: string; } type ColoredAnimalIntersection = Animal & HasColor; function describeAnimal(animal: ColoredAnimalIntersection) { console.log(`The ${animal.name} is ${animal.color} and makes a sound.`); animal.makeSound(); } const coloredCat = new ColoredAnimal(); coloredCat.name = 'Kitty'; coloredCat.color = 'Gray'; describeAnimal(coloredCat);
ColoredAnimalIntersection
类型既是 Animal
类的实例,也拥有 HasColor
接口的 color
属性。
3. 类型防护(Type Guard)
交叉类型在类型防护中也很有用,尤其是当你需要在联合类型中确定一个特定的类型。例如,你可能有一个对象,它可能是两种类型之一,但你希望在某个时刻确定它是哪一种:
typescript interface Movable { move(): void; } interface Static { stay(): void; } type ObjectState = Movable & Static; function isMovable(obj: ObjectState): obj is Movable { return typeof obj.move === 'function'; } const object: ObjectState = { move: () => console.log('Moving...'), stay: () => console.log('Staying...') }; if (isMovable(object)) { object.move(); // 类型防护确保了 move 方法存在 } else { object.stay(); }
isMovable
函数是一个类型保护,它检查 move
方法是否存在,如果存在,则表明 object
实际上是 Movable
类型。
结语
总而言之,TypeScript 的泛型、联合类型和交叉类型为开发者提供了更多的工具和技巧,帮助我们编写更具表达力和可维护性的代码。通过合理运用这些高级类型技巧,我们可以更好地在静态类型的环境中进行开发,减少潜在的错误,并增强代码的可读性和可复用性。
以上就是 TypeScript 高级类型技巧的泛型、联合与交叉类型的详细内容,更多请关注www.mimiwuqi.com的其它相关文章!