类的类型检查
类型检查的基本概念
TypeScript 的类型检查系统是它的核心特性之一。类型检查发生在编译阶段,通过静态分析代码来确保类型的正确性。TypeScript 编译器会检查变量、函数参数、返回值等是否符合预期的类型。
let num: number = 42;
num = "hello"; // 错误:不能将类型"string"分配给类型"number"
原始类型检查
TypeScript 支持 JavaScript 的所有原始类型,并提供了额外的类型检查能力。
// 布尔值
let isDone: boolean = false;
// 数字
let decimal: number = 6;
let hex: number = 0xf00d;
// 字符串
let color: string = "blue";
// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型语法
// 元组
let x: [string, number];
x = ["hello", 10]; // OK
x = [10, "hello"]; // 错误
接口与类型检查
接口是 TypeScript 进行复杂类型检查的主要工具之一。
interface Person {
name: string;
age: number;
address?: string; // 可选属性
readonly id: number; // 只读属性
}
function greet(person: Person) {
return `Hello, ${person.name}`;
}
const user = { name: "Alice", age: 30, id: 1 };
greet(user); // OK
const invalidUser = { name: "Bob" };
greet(invalidUser); // 错误:缺少必需的属性"age"和"id"
类与类型检查
TypeScript 对类的类型检查包括属性、方法和访问修饰符。
class Animal {
private name: string;
protected age: number;
public readonly species: string;
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
// console.log(this.name); // 错误:name是私有的
console.log(this.age); // OK: age是受保护的
}
}
const dog = new Dog("Buddy", 5, "Canine");
dog.move(10);
dog.bark();
// dog.age = 6; // 错误:age是受保护的
泛型的类型检查
泛型提供了创建可重用组件的强大方式,同时保持类型安全。
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString"); // 显式指定类型
let output2 = identity(42); // 类型推断
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = (x, y) => x + y;
高级类型检查
TypeScript 提供了多种高级类型来满足复杂场景的需求。
// 联合类型
let padding: string | number;
padding = "1em"; // OK
padding = 10; // OK
padding = true; // 错误
// 类型别名
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello"; // OK
value = 42; // OK
// 交叉类型
interface Named {
name: string;
}
interface Aged {
age: number;
}
type Person = Named & Aged;
let person: Person = { name: "Alice", age: 30 }; // OK
let invalidPerson: Person = { name: "Bob" }; // 错误:缺少age
// 类型断言
let someValue: any = "this is a string";
let strLength1: number = (<string>someValue).length; // 尖括号语法
let strLength2: number = (someValue as string).length; // as语法
类型守卫与类型推断
TypeScript 使用类型守卫来缩小类型范围,提高类型检查的精确度。
// typeof 类型守卫
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
// instanceof 类型守卫
class Bird {
fly() {
console.log("flying");
}
}
class Fish {
swim() {
console.log("swimming");
}
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
// 自定义类型守卫
interface Cat {
meow(): void;
}
function isCat(animal: any): animal is Cat {
return (animal as Cat).meow !== undefined;
}
function handleAnimal(animal: Bird | Fish | Cat) {
if (isCat(animal)) {
animal.meow();
} else if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
映射类型与条件类型
TypeScript 的映射类型和条件类型提供了强大的类型转换能力。
// 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;
// 条件类型
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<42>; // "number"
type T2 = TypeName<true>; // "boolean"
类型兼容性检查
TypeScript 的类型系统基于结构化类型,具有灵活性但也需要理解其规则。
interface Named {
name: string;
}
class Person {
constructor(public name: string) {}
}
let p: Named;
p = new Person("Alice"); // OK,因为结构兼容
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // 错误:y需要两个参数,x只需要一个
enum Status {
Ready,
Waiting,
}
enum Color {
Red,
Blue,
Green,
}
let status = Status.Ready;
status = Color.Red; // 错误:不同的枚举类型不兼容
声明合并与类型检查
TypeScript 允许声明合并,这会影响类型检查的行为。
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 }; // 合并后的接口
class Album {
label: Album.AlbumLabel;
}
namespace Album {
export class AlbumLabel {}
}
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
export let suffix = "";
export let prefix = "Hello, ";
}
console.log(buildLabel("Sam Smith")); // "Hello, Sam Smith"
模块的类型检查
TypeScript 对模块系统有完整的类型检查支持。
// math.ts
export function square(x: number): number {
return x * x;
}
export const PI = 3.14;
// app.ts
import { square, PI } from "./math";
console.log(square(4)); // 16
console.log(PI); // 3.14
// 类型导入
import type { SomeType } from "./some-module";
let value: SomeType;
// 动态导入
async function load() {
const math = await import("./math");
console.log(math.square(2));
}
装饰器的类型检查
装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问符、属性或参数上。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
实用工具类型
TypeScript 提供了一系列内置的实用工具类型来帮助进行类型转换。
interface Todo {
title: string;
description: string;
completed: boolean;
}
// Partial: 所有属性变为可选
type PartialTodo = Partial<Todo>;
// Readonly: 所有属性变为只读
type ReadonlyTodo = Readonly<Todo>;
// Pick: 选择部分属性
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit: 忽略部分属性
type TodoInfo = Omit<Todo, "completed">;
// Record: 构造一个类型,其属性键为K,属性值为T
type Page = "home" | "about" | "contact";
type PageInfo = Record<Page, { title: string }>;
// Exclude: 从T中排除可以赋值给U的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract: 从T中提取可以赋值给U的类型
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable: 从T中排除null和undefined
type T2 = NonNullable<string | number | undefined>; // string | number
// ReturnType: 获取函数返回值类型
type T3 = ReturnType<() => string>; // string
// InstanceType: 获取构造函数类型的实例类型
class C {
x = 0;
y = 0;
}
type T4 = InstanceType<typeof C>; // C
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn