TypeScript 类型系统详细指南
目录
基础类型
原始类型
// 布尔类型
let isDone: boolean = false;
// 数字类型
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
// 字符串类型
let color: string = "blue";
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${
age + 1
} years old next month.`;
// 空值类型
let unusable: void = undefined;
// Null 和 Undefined
let u: undefined = undefined;
let n: null = null;
// Never 类型 - 表示永不存在的值的类型
function error(message: string): never {
throw new Error(message);
}
// Any 类型 - 任意类型
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
// Unknown 类型 - 类型安全的 any
let value: unknown = 42;
value = "hello";
value = true;
Symbol 和 BigInt
// Symbol 类型
let sym1: symbol = Symbol();
let sym2: symbol = Symbol("key");
// BigInt 类型
let big: bigint = 100n;
对象类型
接口定义
// 基础接口
interface Person {
name: string;
age: number;
}
// 可选属性
interface SquareConfig {
color?: string;
width?: number;
}
// 只读属性
interface Point {
readonly x: number;
readonly y: number;
}
// 索引签名
interface StringArray {
[index: number]: string;
}
interface StringDictionary {
[key: string]: string;
}
// 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
// 类类型接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) {}
}
类型别名
// 基础类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
// 对象类型别名
type User = {
name: string;
age: number;
email?: string;
};
// 函数类型别名
type EventHandler = (event: Event) => void;
数组和元组
数组类型
// 数组类型的两种写法
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
// 只读数组
let ro: ReadonlyArray<number> = [1, 2, 3, 4];
// 多维数组
let matrix: number[][] = [
[1, 2],
[3, 4],
];
元组类型
// 基础元组
let x: [string, number];
x = ["hello", 10]; // 正确
// x = [10, "hello"]; // 错误
// 可选元组元素
type StringNumberPair = [string, number?];
// 剩余元素
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
// 只读元组
type ReadonlyStringNumberPair = readonly [string, number];
// 命名元组
type Range = [start: number, end: number];
函数类型
函数声明和表达式
// 函数声明
function add(x: number, y: number): number {
return x + y;
}
// 函数表达式
let myAdd = function (x: number, y: number): number {
return x + y;
};
// 箭头函数
let myAdd2 = (x: number, y: number): number => x + y;
// 完整的函数类型
let myAdd3: (x: number, y: number) => number = function (
x: number,
y: number
): number {
return x + y;
};
可选参数和默认参数
// 可选参数
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return firstName + " " + lastName;
} else {
return firstName;
}
}
// 默认参数
function buildName2(firstName: string, lastName = "Smith"): string {
return firstName + " " + lastName;
}
// 剩余参数
function buildName3(firstName: string, ...restOfName: string[]): string {
return firstName + " " + restOfName.join(" ");
}
函数重载
// 函数重载声明
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
// 函数实现
function pickCard(x: any): any {
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
} else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
联合类型和交叉类型
联合类型
// 基础联合类型
type StringOrNumber = string | number;
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}'.`);
}
// 字面量联合类型
type Easing = "ease-in" | "ease-out" | "ease-in-out";
// 判别联合类型
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.height * s.width;
case "circle":
return Math.PI * s.radius ** 2;
}
}
交叉类型
// 基础交叉类型
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtworksData {
artworks: { title: string }[];
}
interface ArtistsData {
artists: { name: string }[];
}
// 交叉类型组合
type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;
// Mixin 模式
function extend<First, Second>(first: First, second: Second): First & Second {
const result: Partial<First & Second> = {};
for (const prop in first) {
if (first.hasOwnProperty(prop)) {
(result as First)[prop] = first[prop];
}
}
for (const prop in second) {
if (second.hasOwnProperty(prop)) {
(result as Second)[prop] = second[prop];
}
}
return result as First & Second;
}
字面量类型
字符串字面量类型
// 字符串字面量类型
type EventNames = "click" | "scroll" | "mousemove";
function handleEvent(ele: Element, event: EventNames) {
// ...
}
// 模板字面量类型
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
数字字面量类型
// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
function rollDice(): DiceRoll {
return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}
布尔字面量类型
// 布尔字面量类型
type Success = true;
type Failure = false;
type Result = Success | Failure;
枚举类型
数字枚举
// 基础数字枚举
enum Direction {
Up = 1,
Down,
Left,
Right,
}
// 自动递增
enum Response {
No = 0,
Yes = 1,
}
// 计算成员
enum FileAccess {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
G = "123".length,
}
字符串枚举
// 字符串枚举
enum Direction2 {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
常量枚举
// 常量枚举
const enum Enum {
A = 1,
B = A * 2,
}
泛型
基础泛型
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用泛型
let output = identity<string>("myString");
let output2 = identity("myString"); // 类型推断
// 泛型数组
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
// 或者
function loggingIdentity2<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg;
}
泛型接口
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity2<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity2;
泛型类
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
泛型约束
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity3<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// 在泛型约束中使用类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // 正确
// getProperty(x, "m"); // 错误
条件类型
基础条件类型
// 条件类型语法: T extends U ? X : Y
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<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
分布式条件类型
// 分布式条件类型
type Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;
type T5 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T6 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
infer 关键字
// infer 推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T7 = ReturnType<() => string>; // string
type T8 = ReturnType<(s: string) => void>; // void
// 推断数组元素类型
type Flatten<T> = T extends (infer U)[] ? U : T;
type T9 = Flatten<string[]>; // string
type T10 = Flatten<number>; // number
映射类型
基础映射类型
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 使用示例
interface Person {
name: string;
age: number;
location?: string;
}
type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;
type RequiredPerson = Required<Person>;
键重映射
// 键重映射
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person2 {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person2>;
// {
// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }
// 过滤键
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K];
};
interface Circle2 {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle2>;
// { radius: number; }
工具类型
内置工具类型
// Partial<T> - 将所有属性变为可选
type PartialUser = Partial<User>;
// Required<T> - 将所有属性变为必需
type RequiredUser = Required<User>;
// Readonly<T> - 将所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - 选择指定属性
type UserNameAndAge = Pick<User, "name" | "age">;
// Omit<T, K> - 排除指定属性
type UserWithoutEmail = Omit<User, "email">;
// Exclude<T, U> - 从联合类型中排除
type T11 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U> - 从联合类型中提取
type T12 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> - 排除 null 和 undefined
type T13 = NonNullable<string | number | undefined>; // string | number
// Parameters<T> - 获取函数参数类型
type T14 = Parameters<(a: string, b: number) => void>; // [string, number]
// ConstructorParameters<T> - 获取构造函数参数类型
type T15 = ConstructorParameters<ErrorConstructor>; // [message?: string]
// ReturnType<T> - 获取函数返回类型
type T16 = ReturnType<() => string>; // string
// InstanceType<T> - 获取构造函数实例类型
type T17 = InstanceType<ErrorConstructor>; // Error
// ThisParameterType<T> - 获取函数 this 参数类型
function toHex(this: Number) {
return this.toString(16);
}
type T18 = ThisParameterType<typeof toHex>; // Number
// OmitThisParameter<T> - 移除函数 this 参数
type T19 = OmitThisParameter<typeof toHex>; // () => string
自定义工具类型
// 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 深度可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 获取可选属性
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
// 获取必需属性
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
// 函数重载
type Overload = {
(foo: string): string;
(foo: number): number;
};
类型断言和类型守卫
类型断言
// 角括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法(推荐)
let strLength2: number = (someValue as string).length;
// 非空断言操作符
function liveDangerously(x?: number | null) {
// 没有错误
console.log(x!.toFixed());
}
// 确定赋值断言
let x!: number;
initialize();
console.log(2 * x); // 没有错误
function initialize() {
x = 10;
}
类型守卫
// typeof 类型守卫
function padLeft2(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 类型守卫
interface Padder {
getPaddingString(): string;
}
class SpaceRepeatingPadder implements Padder {
constructor(private numSpaces: number) {}
getPaddingString() {
return Array(this.numSpaces + 1).join(" ");
}
}
class StringPadder implements Padder {
constructor(private value: string) {}
getPaddingString() {
return this.value;
}
}
function getRandomPadder() {
return Math.random() < 0.5
? new SpaceRepeatingPadder(4)
: new StringPadder(" ");
}
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder; // 类型细化为 'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
padder; // 类型细化为 'StringPadder'
}
// 自定义类型守卫
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // pet 被细化为 Fish
} else {
pet.fly(); // pet 被细化为 Bird
}
}
// in 操作符类型守卫
function move2(pet: Fish | Bird) {
if ("swim" in pet) {
pet.swim();
} else {
pet.fly();
}
}
模块和命名空间
模块
// 导出声明
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
// 导出语句
class ZipCodeValidator2 implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export { ZipCodeValidator2 };
export { ZipCodeValidator2 as mainValidator };
// 重新导出
export { ZipCodeValidator as RegExpBasedZipCodeValidator } from "./ZipCodeValidator";
// 默认导出
export default class Calculator {
add(x: number, y: number): number {
return x + y;
}
}
// 导入
import { ZipCodeValidator } from "./ZipCodeValidator";
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
import * as validator from "./ZipCodeValidator";
import "./my-module.js";
命名空间
// 命名空间声明
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
// 使用命名空间
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
高级类型技巧
索引访问类型
// 索引访问类型
type Person3 = { age: number; name: string; alive: boolean };
type Age = Person3["age"]; // number
type I1 = Person3["age" | "name"]; // string | number
type I2 = Person3[keyof Person3]; // string | number | boolean
// 数组索引访问
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person4 = (typeof MyArray)[number]; // { name: string; age: number; }
type Age2 = (typeof MyArray)[number]["age"]; // number
type Age3 = Person4["age"]; // number
键值重映射进阶
// 创建布尔字段
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
// {
// id: string;
// name: string;
// }
// 移除可选修饰符
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User2 = Concrete<MaybeUser>;
// {
// id: string;
// name: string;
// age: number;
// }
递归类型
// JSON 类型定义
type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
// 树结构
interface TreeNode<T> {
value: T;
children?: TreeNode<T>[];
}
// 深度嵌套对象路径
type Paths<T> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? K | `${K}.${Paths<T[K]>}`
: K
: never;
}[keyof T]
: never;
type UserPaths = Paths<{
name: string;
address: {
street: string;
city: string;
country: {
name: string;
code: string;
};
};
}>;
// "name" | "address" | "address.street" | "address.city" | "address.country" | "address.country.name" | "address.country.code"
协变和逆变
// 协变(Covariance)
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 函数返回类型是协变的
type AnimalFunction = () => Animal;
type DogFunction = () => Dog;
// 这是允许的,因为 Dog 是 Animal 的子类型
let animalFunc: AnimalFunction = (() => ({ name: "Animal" })) as DogFunction;
// 逆变(Contravariance)
// 函数参数类型是逆变的
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;
// 这是允许的
let dogHandler: DogHandler = ((animal: Animal) => {
console.log(animal.name);
}) as AnimalHandler;
品牌类型
// 品牌类型(Branded Types)
type Brand<T, U> = T & { __brand: U };
type UserId = Brand<string, "UserId">;
type ProductId = Brand<string, "ProductId">;
function createUserId(id: string): UserId {
return id as UserId;
}
function createProductId(id: string): ProductId {
return id as ProductId;
}
function getUserById(id: UserId): User {
// 实现获取用户逻辑
return {} as User;
}
const userId = createUserId("user123");
const productId = createProductId("product456");
// 正确
getUserById(userId);
// 错误:类型不匹配
// getUserById(productId);
最佳实践
1. 优先使用类型推断
// 好的做法
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
]; // TypeScript 自动推断类型
// 避免不必要的类型注解
const userName = users[0].name; // 自动推断为 string
2. 使用严格的类型检查
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
3. 合理使用联合类型和类型守卫
// 好的做法:使用类型守卫
function processValue(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase(); // TypeScript 知道这里 value 是 string
}
return value.toFixed(2); // TypeScript 知道这里 value 是 number
}
4. 避免使用 any
// 避免
function badFunction(data: any) {
return data.someProperty;
}
// 更好的做法
function goodFunction<T extends { someProperty: unknown }>(data: T) {
return data.someProperty;
}
// 或者使用 unknown
function betterFunction(data: unknown) {
if (typeof data === "object" && data !== null && "someProperty" in data) {
return (data as { someProperty: unknown }).someProperty;
}
throw new Error("Invalid data");
}
5. 使用工具类型简化代码
// 使用内置工具类型
interface User {
id: string;
name: string;
email: string;
age: number;
}
// 创建更新用户的函数
function updateUser(id: string, updates: Partial<User>) {
// 实现更新逻辑
}
// 创建只读用户视图
type ReadonlyUser = Readonly<User>;
总结
TypeScript 的类型系统非常强大和灵活,掌握这些类型特性可以帮助你:
- 提高代码质量:通过静态类型检查捕获潜在错误
- 增强代码可读性:类型作为文档,让代码意图更清晰
- 改善开发体验:获得更好的 IDE 支持和自动补全
- 重构更安全:类型系统帮助确保重构不会破坏现有功能
- 团队协作:统一的类型约定提高团队开发效率
记住,TypeScript 的目标是在不牺牲 JavaScript 灵活性的前提下,提供类型安全。合理使用这些类型特性,可以让你的代码更加健壮和可维护。