TypeScript从入门到精通完全指南:类型系统、接口与泛型实战教程 TypeScript是JavaScript的超集,添加了可选的静态类型和基于类的面向对象编程。由微软开发并维护,TypeScript为大型应用的开发提供了强大的类型检查和代码提示能力。本文将从基础概念到高级特性,全面介绍TypeScript的核心技术。
一、TypeScript基础类型系统 1.1 基本数据类型 TypeScript提供了丰富的基本类型,帮助开发者在编译阶段捕获潜在的错误。
字符串类型:
1 2 3 4 5 6 7 let name : string = "Gene" ;let greeting : string = `Hello, ${name} ` ;let sentence : string = `Hello, my name is ${name} . I'll be learning TypeScript today.` ;
数字与布尔类型:
1 2 3 4 5 6 let age : number = 25 ;let isActive : boolean = true ;let price : number = 19.99 ;let hex : number = 0xff ; let binary : number = 0b1010 ; let octal : number = 0o744 ;
数组类型:
1 2 3 4 5 6 7 8 9 let list1 : number [] = [1 , 2 , 3 ];let list2 : Array <number > = [1 , 2 , 3 ];let names : string [] = ["Alice" , "Bob" , "Charlie" ];let mixed : (string | number )[] = ["a" , 1 , "b" , 2 ];
1.2 特殊类型详解 元组(Tuple):
元组允许定义具有固定数量元素且类型已知的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let person : [string , number ];person = ["Alice" , 25 ]; console .log (person[0 ].substr (1 )); let [userName, userAge] = person;
枚举(Enum):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 enum Color {Red , Green , Blue }let c : Color = Color .Green ; enum Color2 {Red = 1 , Green , Blue }let c2 : Color2 = Color2 .Green ; enum Status { Pending = 0 , Approved = 200 , Rejected = 400 } enum Direction { Up = "UP" , Down = "DOWN" , Left = "LEFT" , Right = "RIGHT" } let colorName : string = Color [1 ];
Any与Unknown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let notSure : any = 4 ;notSure = "maybe a string" ; notSure = false ; notSure.ifItExists (); notSure.toFixed (); let uncertain : unknown = 4 ;if (typeof uncertain === "number" ) { uncertain.toFixed (); }
Void、Null和Undefined:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function warnUser ( ): void { console .log ("This is my warning message" ); } let unusable : void = undefined ;let u : undefined = undefined ;let n : null = null ;
Never类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function error (message : string ): never { throw new Error (message); } function fail ( ) { return error ("Something failed" ); } function infiniteLoop ( ): never { while (true ) {} }
1.3 类型断言 类型断言告诉编译器”相信我,我知道自己在做什么”。
1 2 3 4 5 6 7 8 9 10 let someValue : any = "this is a string" ;let strLength1 : number = (<string >someValue).length ;let strLength2 : number = (someValue as string ).length ;let strLength3 : number = (someValue as any ) as number ;
二、接口与类型别名 2.1 接口基础 接口是TypeScript中定义对象类型的核心方式。
1 2 3 4 5 6 7 8 9 10 11 12 interface LabelledValue { label : string ; } function printLabel (labelledObj : LabelledValue ) { console .log (labelledObj.label ); } let myObj = {size : 10 , label : "Size 10 Object" };printLabel (myObj);
可选属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface SquareConfig { color ?: string ; width ?: number ; } function createSquare (config : SquareConfig ): {color : string ; area : number } { let newSquare = {color : "white" , area : 100 }; if (config.color ) { newSquare.color = config.color ; } if (config.width ) { newSquare.area = config.width * config.width ; } return newSquare; } let mySquare = createSquare ({color : "black" });
只读属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 interface Point { readonly x : number ; readonly y : number ; } let p1 : Point = {x : 10 , y : 20 };let arr : ReadonlyArray <number > = [1 , 2 , 3 ];
2.2 函数类型接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface SearchFunc { (source : string , subString : string ): boolean ; } let mySearch : SearchFunc = function (src : string , sub : string ): boolean { let result = src.search (sub); return result > -1 ; }; let mySearch2 : SearchFunc = function (source : string , substring : string ) { return source.search (substring) > -1 ; };
2.3 索引签名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface StringArray { [index : number ]: string ; } let myArray : StringArray = ["Bob" , "Fred" ];let myStr : string = myArray[0 ];interface NumberDictionary { [index : string ]: number ; length : number ; } interface ReadonlyStringArray { readonly [index : number ]: string ; }
2.4 类实现接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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 ) {} } interface Shape { color : string ; } interface Square extends Shape { sideLength : number ; } let square = {} as Square ;square.color = "blue" ; square.sideLength = 10 ;
2.5 类型别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 type StringOrNumber = string | number ;type Point = {x : number ; y : number };type Name = string ;type NameResolver = () => string ;type NameOrResolver = Name | NameResolver ;type ReturnType = ReturnType <() => string >; type Container <T> = {value : T};
三、泛型编程 3.1 泛型基础 泛型允许创建可重用的组件,支持多种类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 function identity (arg : any ): any { return arg; } function identity<T>(arg : T): T { return arg; } let output1 = identity<string >("myString" );let output2 = identity ("myString" );
泛型约束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface Lengthwise { length : number ; } function loggingIdentity<T extends Lengthwise >(arg : T): T { console .log (arg.length ); return arg; } loggingIdentity ("string" ); loggingIdentity ([1 , 2 , 3 ]);
3.2 泛型接口与类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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; };
3.3 泛型约束与 keyof 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function getProperty<T, K extends keyof T>(obj : T, key : K) { return obj[key]; } let x = { a : 1 , b : 2 , c : 3 };getProperty (x, "a" ); class BeeKeeper { hasMask : boolean ; } class ZooKeeper { nametag : string ; } class Animal { numLegs : number ; } class Bee extends Animal { keeper : BeeKeeper ; } class Lion extends Animal { keeper : ZooKeeper ; } function createInstance<A extends Animal >(c : new () => A): A { return new c (); } createInstance (Lion ).keeper .nametag ;createInstance (Bee ).keeper .hasMask ;
四、高级类型 4.1 联合类型与交叉类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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} '.` ); } interface ErrorHandling { success : boolean ; error ?: { message : string }; } interface ArtworksData { artworks : { title : string }[]; } type ArtworksResponse = ArtworksData & ErrorHandling ;let response : ArtworksResponse = { artworks : [{ title : "Mona Lisa" }], success : true };
4.2 类型守卫 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function isNumber (x : any ): x is number { return typeof x === "number" ; } 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 ; } interface Dog { bark (): void ; } function speak (animal : Cat | Dog ) { if ("meow" in animal) { animal.meow (); } else { animal.bark (); } }
4.3 映射类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface Person { name : string ; age : number ; } type ReadonlyPerson = Readonly <Person >; type PartialPerson = Partial <Person >; type NameOnly = Pick <Person , "name" >; type PageInfo = Record <string , {title : string }>; type Nullable <T> = { [P in keyof T]: T[P] | null };type Proxy <T> = { get (): T; set (value : T): void };type Proxify <T> = { [P in keyof T]: Proxy <T[P]> };
4.4 条件类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 type NonNullable <T> = T extends null | undefined ? never : T;type ReturnType <T> = T extends (...args : any []) => infer R ? R : any ;type ElementType <T> = T extends (infer E)[] ? E : T;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 >; type T1 = TypeName <string []>; type T2 = TypeName <string | number >;
五、模块系统 5.1 模块导出 TypeScript 1.5之后,术语发生了变化:”内部模块”称为”命名空间”,”外部模块”简称为”模块”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 default class DefaultValidator { isAcceptable (s : string ) { return s.length > 0 ; } } class MyValidator { isAcceptable (s : string ) { return s.length > 0 ; } } export = MyValidator ;
5.2 模块导入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import { ZipCodeValidator } from "./ZipCodeValidator" ;import { ZipCodeValidator as ZCV } from "./ZipCodeValidator" ;import * as validator from "./ZipCodeValidator" ;let myValidator = new validator.ZipCodeValidator ();import DefaultValidator from "./DefaultValidator" ;import DefaultValidator , { ZipCodeValidator } from "./Validators" ;import zip = require ("./ZipCodeValidator" );let validator2 = new zip ();import type { SomeType } from "./someModule" ;
5.3 命名空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 (); import val = Validation ;let validator3 = new val.ZipCodeValidator ();
六、装饰器 6.1 方法装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function log (target : any , propertyKey : string , descriptor : PropertyDescriptor ) { const originalMethod = descriptor.value ; descriptor.value = function (...args : any [] ) { console .log (`Calling ${propertyKey} with` , args); const result = originalMethod.apply (this , args); console .log (`Result: ${result} ` ); return result; }; return descriptor; } class MyClass { @log add (a : number , b : number ): number { return a + b; } } const obj = new MyClass ();obj.add (1 , 2 );
6.2 类装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function sealed (constructor : Function ) { Object .seal (constructor); Object .seal (constructor.prototype ); } @sealed class Greeter { greeting : string ; constructor (message : string ) { this .greeting = message; } greet ( ) { return "Hello, " + this .greeting ; } } function decoratorFactory (name : string ) { return function (constructor : Function ) { console .log (`Decorator called with name: ${name} ` ); }; } @decoratorFactory ("example" )class Example {}
6.3 属性装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function format (formatString : string ) { return function (target : any , propertyKey : string ) { let value = target[propertyKey]; const getter = function ( ) { return `${formatString} ${value} ` ; }; const setter = function (newVal : string ) { value = newVal; }; Object .defineProperty (target, propertyKey, { get : getter, set : setter, enumerable : true , configurable : true }); }; } class FormattedClass { @format ("Name:" ) name : string = "John" ; } const formatted = new FormattedClass ();console .log (formatted.name );
七、配置与工具 7.1 tsconfig.json配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "compilerOptions" : { "target" : "ES2020" , "module" : "ESNext" , "moduleResolution" : "node" , "strict" : true , "esModuleInterop" : true , "skipLibCheck" : true , "forceConsistentCasingInFileNames" : true , "outDir" : "./dist" , "rootDir" : "./src" , "declaration" : true , "declarationMap" : true , "sourceMap" : true } , "include" : [ "src/**/*" ] , "exclude" : [ "node_modules" , "dist" ] }
7.2 常见编译选项
选项
说明
target
编译目标版本(ES3/ES5/ES6/ES2020等)
module
模块系统(CommonJS/AMD/UMD/ES6等)
strict
启用所有严格类型检查选项
noImplicitAny
禁止隐式any类型
strictNullChecks
启用严格的null检查
outDir
输出目录
rootDir
源码根目录
declaration
生成.d.ts声明文件
sourceMap
生成source map文件
八、最佳实践 8.1 类型定义建议 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 interface User { id : number ; name : string ; } type ID = string | number ;type Status = "pending" | "approved" | "rejected" ;function processItems<T>(items : T[]): T[] { return items.filter (item => item !== null ); } interface Todo { title : string ; description : string ; completed : boolean ; } type TodoPreview = Pick <Todo , "title" | "completed" >;type TodoInfo = Omit <Todo , "completed" >;
8.2 类型安全技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let config = { host : "localhost" , port : 3000 } as const ; function isString (value : unknown ): value is string { return typeof value === "string" ; } function processUser (user ?: User ) { const name = user!.name ; } const userName = user?.name ?? "Anonymous" ;
九、总结 TypeScript通过静态类型检查大大提升了JavaScript代码的可维护性和可靠性。本文涵盖了TypeScript的核心特性:
类型系统 :基本类型、联合类型、交叉类型、类型推断
接口与类型别名 :定义对象结构、可选属性、只读属性
泛型编程 :类型参数、约束、映射类型、条件类型
模块系统 :ES模块、CommonJS模块、命名空间
装饰器 :类装饰器、方法装饰器、属性装饰器
高级类型 :类型守卫、映射类型、条件类型
掌握TypeScript的关键在于理解类型系统的思维方式,善用类型推断减少冗余代码,同时利用静态类型检查在编译阶段捕获错误。随着项目规模的增长,TypeScript带来的类型安全优势会越来越明显。
建议学习者:
从基础类型开始,逐步掌握接口和泛型
多阅读优秀开源项目的TypeScript代码
使用strict模式培养良好的类型习惯
关注TypeScript的更新和新特性