|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. TypeScript简介
1.1 什么是TypeScript
TypeScript是由微软开发的一种开源编程语言,它是JavaScript的超集,添加了静态类型系统。TypeScript在编译时会被转换为纯JavaScript代码,这意味着任何现有的JavaScript代码都可以在TypeScript中运行,同时TypeScript还提供了额外的功能,如类型检查、接口、枚举等。
1.2 TypeScript与JavaScript的关系
TypeScript是JavaScript的超集,这意味着所有有效的JavaScript代码都是有效的TypeScript代码。TypeScript扩展了JavaScript,添加了类型系统和其他功能,这些功能在编译时会被移除,最终生成标准的JavaScript代码。
1.3 TypeScript的优势
1. 静态类型检查:在编译时捕获错误,而不是在运行时
2. 更好的IDE支持:提供代码补全、导航和重构功能
3. 增强的可读性和可维护性:类型注解使代码更易于理解
4. 先进的JavaScript特性:支持最新的ECMAScript特性
5. 大型项目的理想选择:通过类型系统帮助管理复杂项目
1.4 开发环境搭建
要开始使用TypeScript,你需要安装Node.js和TypeScript编译器:
- # 安装Node.js (如果尚未安装)
- # 访问 https://nodejs.org/ 下载并安装
- # 安装TypeScript编译器
- npm install -g typescript
- # 验证安装
- tsc --version
复制代码
创建你的第一个TypeScript文件:
- // hello.ts
- function greet(name: string): string {
- return `Hello, ${name}!`;
- }
- const message = greet("TypeScript");
- console.log(message);
复制代码
编译并运行:
- tsc hello.ts
- node hello.js
复制代码
2. TypeScript基础语法
2.1 基本类型
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";
- color = 'red';
- // 数组类型
- let list: number[] = [1, 2, 3];
- // 或者使用泛型
- let list2: Array<number> = [1, 2, 3];
- // 元组类型 - 允许表示一个已知元素数量和类型的数组
- let x: [string, number];
- x = ["hello", 10]; // 正确
- // x = [10, "hello"]; // 错误
- // 枚举类型
- enum Color {Red, Green, Blue}
- let c: Color = Color.Green;
- // Any类型 - 不希望类型检查器对这些值进行检查
- let notSure: any = 4;
- notSure = "maybe a string";
- notSure = false;
- // Void类型 - 表示没有任何类型
- function warnUser(): void {
- console.log("This is a warning message");
- }
- // Null 和 Undefined
- let u: undefined = undefined;
- let n: null = null;
- // Never类型 - 表示永不存在的值的类型
- function error(message: string): never {
- throw new Error(message);
- }
- // Object类型
- let obj: object = {name: "TypeScript"};
复制代码
2.2 变量声明
TypeScript支持JavaScript的变量声明方式,并添加了类型注解:
- // 使用var声明(不推荐,有作用域问题)
- var myVar: string = "Hello";
- // 使用let声明(推荐,块级作用域)
- let myLet: string = "Hello";
- // 使用const声明(推荐,常量)
- const myConst: string = "Hello";
- // 解构赋值
- let input = [1, 2];
- let [first, second] = input;
- // 对象解构
- let o = {
- a: "foo",
- b: 12,
- c: "bar"
- };
- let {a, b} = o;
- // 类型断言
- let someValue: any = "this is a string";
- let strLength: number = (someValue as string).length;
- // 或者使用尖括号语法
- let strLength2: number = (<string>someValue).length;
复制代码
2.3 函数
TypeScript为函数添加了类型注解,使函数更加安全:
- // 基本函数类型
- function add(x: number, y: number): number {
- return x + y;
- }
- // 完整函数类型
- let myAdd: (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: string = "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 {
- 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 };
- }
- }
复制代码
2.4 接口
接口是TypeScript的核心概念之一,用于定义对象的结构:
- // 基本接口
- interface Person {
- name: string;
- age: number;
- }
- function greet(person: Person): string {
- return "Hello, " + person.name;
- }
- // 可选属性
- interface SquareConfig {
- color?: string;
- width?: number;
- }
- // 只读属性
- interface Point {
- readonly x: number;
- readonly y: number;
- }
- // 函数类型
- interface SearchFunc {
- (source: string, subString: string): boolean;
- }
- // 可索引类型
- interface StringArray {
- [index: number]: string;
- }
- // 类类型
- 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;
- }
复制代码
2.5 类
TypeScript中的类是对ES6类的扩展,添加了类型和其他特性:
- // 基本类
- class Greeter {
- greeting: string;
- constructor(message: string) {
- this.greeting = message;
- }
- greet(): string {
- return "Hello, " + this.greeting;
- }
- }
- // 继承
- class Animal {
- name: string;
- constructor(theName: string) { this.name = theName; }
- move(distanceInMeters: number = 0) {
- console.log(`${this.name} moved ${distanceInMeters}m.`);
- }
- }
- class Snake extends Animal {
- constructor(name: string) { super(name); }
- move(distanceInMeters = 5) {
- console.log("Slithering...");
- super.move(distanceInMeters);
- }
- }
- // 公共、私有与受保护的修饰符
- class Animal2 {
- public name: string;
- private privateName: string;
- protected protectedName: string;
- constructor(theName: string) {
- this.name = theName;
- this.privateName = theName;
- this.protectedName = theName;
- }
- }
- // 只读属性
- class Octopus {
- readonly name: string;
- readonly numberOfLegs: number = 8;
- constructor(theName: string) {
- this.name = theName;
- }
- }
- // 存取器
- let passcode = "secret passcode";
- class Employee {
- private _fullName: string;
- get fullName(): string {
- return this._fullName;
- }
- set fullName(newName: string) {
- if (passcode && passcode === "secret passcode") {
- this._fullName = newName;
- } else {
- console.log("Error: Unauthorized update of employee!");
- }
- }
- }
- // 静态属性
- class Grid {
- static origin = {x: 0, y: 0};
- calculateDistanceFromOrigin(point: {x: number; y: number;}) {
- let xDist = (point.x - Grid.origin.x);
- let yDist = (point.y - Grid.origin.y);
- return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
- }
- constructor(public scale: number) { }
- }
- // 抽象类
- abstract class Animal3 {
- abstract makeSound(): void;
- move(): void {
- console.log("roaming the earth...");
- }
- }
- class Dog extends Animal3 {
- makeSound(): void {
- console.log("Woof! Woof!");
- }
- }
复制代码
2.6 泛型基础
泛型允许创建可重用的组件,一个组件可以支持多种类型的数据:
- // 基本泛型
- function identity<T>(arg: T): T {
- return arg;
- }
- // 使用泛型
- let output = identity<string>("myString");
- let output2 = identity("myString"); // 类型推断
- // 泛型接口
- interface GenericIdentityFn {
- <T>(arg: T): T;
- }
- // 泛型类
- 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 loggingIdentity<T extends Lengthwise>(arg: T): T {
- console.log(arg.length); // 现在我们知道arg具有length属性
- return arg;
- }
复制代码
3. 中级特性
3.1 高级类型
TypeScript提供了多种高级类型,使类型系统更加灵活和强大:
- // 交叉类型
- interface BusinessPartner {
- name: string;
- credit: number;
- }
- interface Identity {
- id: number;
- name: string;
- }
- type Employee = BusinessPartner & Identity;
- // 联合类型
- 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 Name = string;
- type NameResolver = () => string;
- type NameOrResolver = Name | NameResolver;
- function getName(n: NameOrResolver): Name {
- if (typeof n === "string") {
- return n;
- } else {
- return n();
- }
- }
- // 字面量类型
- 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): number {
- switch (s.kind) {
- case "square": return s.size * s.size;
- case "rectangle": return s.width * s.height;
- case "circle": return Math.PI * s.radius ** 2;
- }
- }
- // 索引类型
- function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
- return names.map(n => o[n]);
- }
- interface Person {
- name: string;
- age: number;
- }
- let person: Person = {
- name: 'Jarid',
- age: 35
- };
- let strings: string[] = pluck(person, ['name']); // ok, string[]
复制代码
3.2 类型守卫
类型守卫允许你在特定的代码块中缩小类型的范围:
- // 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("bird flies");
- }
- }
- class Fish {
- swim() {
- console.log("fish swims");
- }
- }
- function pet(pet: Bird | Fish) {
- if (pet instanceof Bird) {
- pet.fly();
- } else if (pet instanceof Fish) {
- pet.swim();
- }
- }
- // 自定义类型守卫
- function isFish(pet: Fish | Bird): pet is Fish {
- return (<Fish>pet).swim !== undefined;
- }
- // 可null类型
- function f(sn: string | null): string {
- if (sn === null) {
- return "default";
- } else {
- return sn;
- }
- }
复制代码
3.3 装饰器
装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问符、属性或参数上:
- // 类装饰器
- 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 enumerable(value: boolean) {
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
- descriptor.enumerable = value;
- };
- }
- class Greeter2 {
- greeting: string;
- constructor(message: string) {
- this.greeting = message;
- }
- @enumerable(false)
- greet() {
- return "Hello, " + this.greeting;
- }
- }
- // 访问器装饰器
- class Point {
- private _x: number;
- private _y: number;
- constructor(x: number, y: number) {
- this._x = x;
- this._y = y;
- }
- @configurable(false)
- get x() { return this._x; }
- @configurable(false)
- get y() { return this._y; }
- }
- function configurable(value: boolean) {
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
- descriptor.configurable = value;
- };
- }
- // 属性装饰器
- class Greeter3 {
- @format("Hello, %s")
- greeting: string;
- constructor(message: string) {
- this.greeting = message;
- }
- greet() {
- let greetingString = this.greeting;
- return greetingString;
- }
- }
- import "reflect-metadata";
- function format(formatString: string) {
- return function (target: any, propertyKey: string): void {
- Reflect.defineMetadata("format", formatString, target, propertyKey);
- };
- }
- // 参数装饰器
- class Greeter4 {
- greeting: string;
- constructor(message: string) {
- this.greeting = message;
- }
- @validate
- greet(@required name: string) {
- return "Hello " + name + ", " + this.greeting;
- }
- }
- import "reflect-metadata";
- function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
- let requiredParams: number[] = Reflect.getOwnMetadata("required", target, propertyKey) || [];
- requiredParams.push(parameterIndex);
- Reflect.defineMetadata("required", requiredParams, target, propertyKey);
- }
- function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) {
- let method = descriptor.value;
-
- descriptor.value = function () {
- let requiredParams: number[] = Reflect.getOwnMetadata("required", target, propertyName);
- if (requiredParams) {
- for (let parameterIndex of requiredParams) {
- if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {
- throw new Error("Missing required argument.");
- }
- }
- }
-
- return method.apply(this, arguments);
- }
- }
复制代码
3.4 模块系统
TypeScript支持ES模块和CommonJS模块系统:
- // 导出
- // 导出声明
- export interface StringValidator {
- isAcceptable(s: string): boolean;
- }
- // 导出语句
- export class ZipCodeValidator implements StringValidator {
- isAcceptable(s: string) {
- return s.length === 5 && numberRegexp.test(s);
- }
- }
- // 导入
- import { StringValidator } from "./StringValidator";
- import { ZipCodeValidator } from "./ZipCodeValidator";
- // 默认导出
- export default class ZipCodeValidator {
- // ...
- }
- // 导入默认导出
- import ZipCodeValidator from "./ZipCodeValidator";
- // 导出所有
- export * from "./StringValidator";
- // 重新导出
- export { ZipCodeValidator as RegExpBasedZipCodeValidator } from "./ZipCodeValidator";
复制代码
3.5 命名空间
命名空间是TypeScript中组织代码的一种方式,特别适用于避免全局命名冲突:
- // 基本命名空间
- 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();
- // 嵌套命名空间
- namespace Shapes {
- namespace Polygons {
- export class Triangle { }
- export class Square { }
- }
- }
- // 使用嵌套命名空间
- let sq = new Shapes.Polygons.Square();
- // 命名空间别名
- import polygons = Shapes.Polygons;
- let tri = new polygons.Triangle();
复制代码
4. 高级特性
4.1 高级泛型
TypeScript的泛型系统非常强大,支持复杂的类型操作:
- // 泛型约束与索引类型
- function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
- return obj[key];
- }
- let x = { a: 1, b: 2, c: 3, d: 4 };
- getProperty(x, "a"); // okay
- getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
- // 泛型与new
- function create<T>(c: { new(): T; }): T {
- return new c();
- }
- // 泛型与默认类型
- interface GenericIdentityFn<T = string> {
- (arg: T): 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>; // "string"
- type T1 = TypeName<"a">; // "string"
- type T2 = TypeName<true>; // "boolean"
- type T3 = TypeName<() => void>; // "function"
- type T4 = TypeName<string[]>; // "object"
- // 泛型与映射类型
- type Readonly<T> = {
- readonly [P in keyof T]: T[P];
- };
- type Partial<T> = {
- [P in keyof T]?: T[P];
- };
- type Person = {
- name: string;
- age: number;
- };
- type ReadonlyPerson = Readonly<Person>;
- type PartialPerson = Partial<Person>;
复制代码
4.2 条件类型
条件类型允许根据条件选择类型:
- // 基本条件类型
- type NonNullable<T> = T extends null | undefined ? never : T;
- // 分布式条件类型
- type ToArray<T> = T extends any ? T[] : never;
- type StrOrNumArray = ToArray<string | number>; // string[] | number[]
- // 条件类型与推断
- type ExtractType<T> = T extends Promise<infer U> ? U : never;
- type PromiseType = ExtractType<Promise<string>>; // string
- // 条件类型与映射
- type ExtractInfo<T> = {
- [K in keyof T]: T[K] extends Function ? K : never;
- }[keyof T];
- interface User {
- id: number;
- name: string;
- updateName: (newName: string) => void;
- }
- type FunctionKeys = ExtractInfo<User>; // "updateName"
复制代码
4.3 映射类型
映射类型允许基于现有类型创建新类型:
- // 基本映射类型
- type OptionsFlags<T> = {
- [K in keyof T]: boolean;
- };
- interface FeatureFlags {
- darkMode: () => void;
- newUserProfile: () => void;
- }
- type FeatureOptions = OptionsFlags<FeatureFlags>;
- // 等同于:
- // {
- // darkMode: boolean;
- // newUserProfile: boolean;
- // }
- // 修改属性
- type CreateMutable<T> = {
- -readonly [P in keyof T]: T[P];
- };
- type LockedAccount = {
- readonly id: string;
- readonly name: string;
- };
- type UnlockedAccount = CreateMutable<LockedAccount>;
- // 等同于:
- // {
- // id: string;
- // name: string;
- // }
- // 添加属性修饰符
- type Concrete<T> = {
- [P in keyof T]-?: T[P];
- };
- type MaybeUser = {
- id: string;
- name?: string;
- age?: number;
- };
- type User = Concrete<MaybeUser>;
- // 等同于:
- // {
- // id: string;
- // name: string;
- // age: number;
- // }
- // 通过as重新映射键
- type Getters<T> = {
- [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
- };
- interface Person {
- name: string;
- age: number;
- location: string;
- }
- type LazyPerson = Getters<Person>;
- // 等同于:
- // {
- // getName: () => string;
- // getAge: () => number;
- // getLocation: () => string;
- // }
复制代码
4.4 模板字面量类型
模板字面量类型允许基于字符串操作创建类型:
- // 基本模板字面量类型
- 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 Email = "email";
- type Locale = "en" | "de" | "fr";
- type EmailLocaleIDs = `${Email}_${Locale}`;
- // 等同于:
- // "email_en" | "email_de" | "email_fr"
- // 大小写转换
- type Greeting = "Hello, World";
- type ShoutyGreeting = Uppercase<Greeting>;
- // 等同于:
- // "HELLO, WORLD"
- type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`;
- type MainID = ASCIICacheKey<"my_app">;
- // 等同于:
- // "ID-MY_APP"
- // 字符串操作类型
- type Uncapitalize<S extends string> = intrinsic;
- type Capitalize<S extends string> = intrinsic;
- type Uppercase<S extends string> = intrinsic;
- type Lowercase<S extends string> = intrinsic;
复制代码
4.5 类型推断与控制流分析
TypeScript的类型推断和控制流分析能力非常强大:
- // 类型推断
- let x = 3; // 推断为number类型
- // 上下文类型
- window.onmousedown = function(mouseEvent) {
- console.log(mouseEvent.button); // 推断为MouseEvent类型
- };
- // 类型保护与控制流分析
- function padLeft(value: string, padding: string | number) {
- if (typeof padding === "number") {
- return Array(padding + 1).join(" ") + value;
- }
- return padding + value;
- }
- // 可辨识联合与控制流分析
- 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): number {
- switch (s.kind) {
- case "square": return s.size * s.size;
- case "rectangle": return s.width * s.height;
- case "circle": return Math.PI * s.radius ** 2;
- default:
- // 如果没有处理所有情况,这里会报错
- const _exhaustiveCheck: never = s;
- return _exhaustiveCheck;
- }
- }
- // 类型推断与null检查
- function doSomething(x: string | null) {
- if (x === null) {
- // 在这个块中,x的类型是null
- } else {
- // 在这个块中,x的类型是string
- console.log(x.toUpperCase());
- }
- }
- // 类型推断与赋值分析
- let x: string | number | boolean;
- x = "hello"; // x现在是string类型
- x = 42; // x现在是number类型
- x = true; // x现在是boolean类型
复制代码
5. 工程实践
5.1 配置文件详解
TypeScript的配置文件tsconfig.json是项目中的关键文件:
- {
- "compilerOptions": {
- /* 基本选项 */
- "target": "es5", // 指定ECMAScript目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'
- "module": "commonjs", // 指定模块代码生成: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'
- "lib": ["es6", "dom"], // 指定要包含在编译中的库文件
- "allowJs": true, // 允许编译javascript文件
- "checkJs": true, // 在.js文件中报告错误
- "jsx": "react", // 指定JSX代码生成: 'preserve', 'react-native', or 'react'
- "declaration": true, // 生成相应的 '.d.ts' 文件
- "sourceMap": true, // 生成相应的 '.map' 文件
- "outFile": "./", // 连接输出到单个文件
- "outDir": "./", // 重定向输出结构到目录
- "rootDir": "./", // 指定输入文件的基本目录
- "removeComments": true, // 不输出注释到输出
- "noEmit": true, // 不输出文件
- "importHelpers": true, // 从tslib导入辅助工具函数
- "downlevelIteration": true, // 在ES3和ES5中完全支持迭代
- "isolatedModules": true, // 将每个文件作为单独的模块
- "strict": true, // 启用所有严格类型检查选项
-
- /* 额外检查 */
- "noUnusedLocals": true, // 报告未使用的局部变量的错误
- "noUnusedParameters": true, // 报告未使用的参数的错误
- "noImplicitReturns": true, // 在函数中没有隐式返回时报告错误
- "noFallthroughCasesInSwitch": true, // 在switch语句中报告fallthrough情况的错误
-
- /* 模块解析选项 */
- "moduleResolution": "node", // 指定模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
- "baseUrl": "./", // 用于解析非相对模块名称的基本目录
- "paths": {}, // 模块名到基于baseUrl的路径映射
- "rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的内容结构
- "typeRoots": [], // 包含类型声明的文件夹列表
- "types": [], // 要包含的类型声明文件名列表
- "allowSyntheticDefaultImports": true, // 允许从没有默认导出的模块中默认导入
-
- /* Source Map Options */
- "sourceRoot": "", // 指定调试器应定位的位置,而不是生成的源位置
- "mapRoot": "", // 指定调试器应定位映射文件的位置,而不是生成的位置
- "inlineSourceMap": true, // 生成带有源映射的单个文件,而不是单独的文件
- "inlineSources": true, // 将源代码与源映射一起内联到单个文件中
-
- /* 实验性选项 */
- "experimentalDecorators": true, // 启用对ES装饰器的实验性支持
- "emitDecoratorMetadata": true // 为装饰器发出设计类型元数据
- },
- "include": [
- "src/**/*"
- ],
- "exclude": [
- "node_modules",
- "**/*.spec.ts"
- ]
- }
复制代码
5.2 与框架集成
TypeScript可以与现代前端框架无缝集成:
- // 安装类型定义
- npm install --save-dev @types/react @types/react-dom
- // 基本函数组件
- import React from 'react';
- interface GreetingProps {
- name: string;
- }
- const Greeting: React.FC<GreetingProps> = ({ name }) => {
- return <h1>Hello, {name}!</h1>;
- };
- export default Greeting;
- // 类组件
- import React from 'react';
- interface CounterState {
- count: number;
- }
- class Counter extends React.Component<{}, CounterState> {
- constructor(props: {}) {
- super(props);
- this.state = {
- count: 0
- };
- }
- increment = () => {
- this.setState({ count: this.state.count + 1 });
- };
- render() {
- return (
- <div>
- <p>Count: {this.state.count}</p>
- <button onClick={this.increment}>Increment</button>
- </div>
- );
- }
- }
- export default Counter;
- // 使用Hooks
- import React, { useState, useEffect } from 'react';
- interface User {
- id: number;
- name: string;
- email: string;
- }
- const UserProfile: React.FC<{ userId: number }> = ({ userId }) => {
- const [user, setUser] = useState<User | null>(null);
- const [loading, setLoading] = useState<boolean>(true);
- useEffect(() => {
- const fetchUser = async () => {
- try {
- setLoading(true);
- const response = await fetch(`https://api.example.com/users/${userId}`);
- const userData = await response.json();
- setUser(userData);
- } catch (error) {
- console.error('Error fetching user:', error);
- } finally {
- setLoading(false);
- }
- };
- fetchUser();
- }, [userId]);
- if (loading) return <div>Loading...</div>;
- if (!user) return <div>User not found</div>;
- return (
- <div>
- <h2>{user.name}</h2>
- <p>Email: {user.email}</p>
- </div>
- );
- };
- export default UserProfile;
复制代码- // 安装类型定义
- npm install --save-dev vue-class-component vue-property-decorator
- // 使用装饰器
- import { Vue, Component, Prop } from 'vue-property-decorator';
- @Component
- export default class Greeting extends Vue {
- @Prop({ type: String, required: true })
- name!: string;
- get greeting(): string {
- return `Hello, ${this.name}!`;
- }
- }
- // 使用Composition API
- import { defineComponent, ref, computed } from 'vue';
- export default defineComponent({
- props: {
- name: {
- type: String,
- required: true
- }
- },
- setup(props) {
- const count = ref(0);
-
- const doubleCount = computed(() => count.value * 2);
-
- function increment() {
- count.value++;
- }
-
- return {
- count,
- doubleCount,
- increment
- };
- }
- });
- // 使用TypeScript与Vue 3的<script setup>
- <script setup lang="ts">
- import { ref, computed } from 'vue';
- interface Props {
- name: string;
- initialCount?: number;
- }
- const props = withDefaults(defineProps<Props>(), {
- initialCount: 0
- });
- const count = ref(props.initialCount);
- const doubleCount = computed(() => count.value * 2);
- function increment() {
- count.value++;
- }
- </script>
复制代码
5.3 代码规范与最佳实践
使用TypeScript时,遵循一些最佳实践可以提高代码质量和可维护性:
5.4 调试技巧
在TypeScript中进行调试时,有一些技巧可以帮助你更有效地解决问题:
- // 1. 使用类型断言进行调试
- function processValue(value: unknown) {
- // 使用类型断言检查类型
- if ((value as string).toUpperCase) {
- console.log("It's a string");
- } else if ((value as number).toFixed) {
- console.log("It's a number");
- }
- }
- // 2. 使用条件类型进行类型检查
- type IsString<T> = T extends string ? true : false;
- type CheckString = IsString<"hello">; // true
- type CheckNumber = IsString<42>; // false
- // 3. 使用类型守卫进行运行时检查
- function isString(value: unknown): value is string {
- return typeof value === 'string';
- }
- function processValue(value: unknown) {
- if (isString(value)) {
- // 在这个块中,TypeScript知道value是string类型
- console.log(value.toUpperCase());
- }
- }
- // 4. 使用类型谓词进行高级类型检查
- interface Bird {
- fly(): void;
- layEggs(): void;
- }
- interface Fish {
- swim(): void;
- layEggs(): void;
- }
- function isFish(pet: Fish | Bird): pet is Fish {
- return (pet as Fish).swim !== undefined;
- }
- function processPet(pet: Fish | Bird) {
- if (isFish(pet)) {
- pet.swim();
- } else {
- pet.fly();
- }
- }
- // 5. 使用映射类型进行调试
- type Optional<T> = {
- [K in keyof T]?: T[K];
- };
- interface User {
- id: number;
- name: string;
- email: string;
- }
- type PartialUser = Optional<User>;
- // 6. 使用条件类型和infer进行类型推断
- type Unpack<T> = T extends Array<infer U> ? U : T;
- type UnpackedString = Unpack<string[]>; // string
- type UnpackedNumber = Unpack<number>; // number
- // 7. 使用类型断言和类型保护处理DOM元素
- function getElementById<T extends HTMLElement>(id: string): T | null {
- const element = document.getElementById(id);
- return element as T;
- }
- const button = getElementById<HTMLButtonElement>('my-button');
- if (button) {
- button.disabled = true;
- }
- // 8. 使用类型查询获取变量类型
- const user = {
- id: 1,
- name: 'John',
- email: 'john@example.com'
- };
- type User = typeof user;
复制代码
6. 实战项目
6.1 构建一个完整的TypeScript应用
让我们构建一个简单的任务管理应用,展示TypeScript的实际应用:
- // 1. 定义类型和接口
- interface Task {
- id: number;
- title: string;
- description: string;
- completed: boolean;
- createdAt: Date;
- updatedAt: Date;
- }
- interface TaskService {
- getAll(): Promise<Task[]>;
- getById(id: number): Promise<Task | null>;
- create(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
- update(id: number, updates: Partial<Task>): Promise<Task | null>;
- delete(id: number): Promise<boolean>;
- }
- // 2. 实现任务服务
- class LocalStorageTaskService implements TaskService {
- private readonly STORAGE_KEY = 'tasks';
- async getAll(): Promise<Task[]> {
- const tasksJson = localStorage.getItem(this.STORAGE_KEY);
- if (!tasksJson) return [];
-
- const tasks = JSON.parse(tasksJson);
- return tasks.map((task: any) => ({
- ...task,
- createdAt: new Date(task.createdAt),
- updatedAt: new Date(task.updatedAt)
- }));
- }
- async getById(id: number): Promise<Task | null> {
- const tasks = await this.getAll();
- return tasks.find(task => task.id === id) || null;
- }
- async create(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
- const tasks = await this.getAll();
- const newId = tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1;
-
- const newTask: Task = {
- id: newId,
- ...taskData,
- createdAt: new Date(),
- updatedAt: new Date()
- };
-
- tasks.push(newTask);
- this.saveTasks(tasks);
-
- return newTask;
- }
- async update(id: number, updates: Partial<Task>): Promise<Task | null> {
- const tasks = await this.getAll();
- const taskIndex = tasks.findIndex(task => task.id === id);
-
- if (taskIndex === -1) return null;
-
- tasks[taskIndex] = {
- ...tasks[taskIndex],
- ...updates,
- updatedAt: new Date()
- };
-
- this.saveTasks(tasks);
- return tasks[taskIndex];
- }
- async delete(id: number): Promise<boolean> {
- const tasks = await this.getAll();
- const filteredTasks = tasks.filter(task => task.id !== id);
-
- if (filteredTasks.length === tasks.length) return false;
-
- this.saveTasks(filteredTasks);
- return true;
- }
- private saveTasks(tasks: Task[]): void {
- localStorage.setItem(this.STORAGE_KEY, JSON.stringify(tasks));
- }
- }
- // 3. 创建任务管理器类
- class TaskManager {
- constructor(private taskService: TaskService) {}
- async addTask(title: string, description: string): Promise<Task> {
- return this.taskService.create({
- title,
- description,
- completed: false
- });
- }
- async completeTask(id: number): Promise<Task | null> {
- return this.taskService.update(id, { completed: true });
- }
- async updateTask(id: number, updates: Partial<Omit<Task, 'id' | 'createdAt' | 'updatedAt'>>): Promise<Task | null> {
- return this.taskService.update(id, updates);
- }
- async removeTask(id: number): Promise<boolean> {
- return this.taskService.delete(id);
- }
- async getTasks(): Promise<Task[]> {
- return this.taskService.getAll();
- }
- async getTask(id: number): Promise<Task | null> {
- return this.taskService.getById(id);
- }
- }
- // 4. 创建UI控制器
- class TaskUIController {
- private taskList: HTMLElement;
- private taskForm: HTMLFormElement;
- private taskTitleInput: HTMLInputElement;
- private taskDescriptionInput: HTMLTextAreaElement;
-
- constructor(
- private taskManager: TaskManager,
- private container: HTMLElement
- ) {
- this.setupUI();
- this.attachEventListeners();
- this.loadTasks();
- }
- private setupUI(): void {
- this.container.innerHTML = `
- <div class="task-app">
- <h1>Task Manager</h1>
- <form id="task-form" class="task-form">
- <div class="form-group">
- <label for="task-title">Title</label>
- <input type="text" id="task-title" required>
- </div>
- <div class="form-group">
- <label for="task-description">Description</label>
- <textarea id="task-description" rows="3"></textarea>
- </div>
- <button type="submit">Add Task</button>
- </form>
- <div id="task-list" class="task-list"></div>
- </div>
- `;
- this.taskList = document.getElementById('task-list')!;
- this.taskForm = document.getElementById('task-form')! as HTMLFormElement;
- this.taskTitleInput = document.getElementById('task-title')! as HTMLInputElement;
- this.taskDescriptionInput = document.getElementById('task-description')! as HTMLTextAreaElement;
- }
- private attachEventListeners(): void {
- this.taskForm.addEventListener('submit', (e) => {
- e.preventDefault();
- this.handleAddTask();
- });
- }
- private async handleAddTask(): Promise<void> {
- const title = this.taskTitleInput.value.trim();
- const description = this.taskDescriptionInput.value.trim();
-
- if (!title) return;
-
- try {
- await this.taskManager.addTask(title, description);
- this.taskForm.reset();
- this.loadTasks();
- } catch (error) {
- console.error('Error adding task:', error);
- }
- }
- private async loadTasks(): Promise<void> {
- try {
- const tasks = await this.taskManager.getTasks();
- this.renderTasks(tasks);
- } catch (error) {
- console.error('Error loading tasks:', error);
- }
- }
- private renderTasks(tasks: Task[]): void {
- this.taskList.innerHTML = '';
-
- if (tasks.length === 0) {
- this.taskList.innerHTML = '<p>No tasks found. Add your first task!</p>';
- return;
- }
-
- tasks.forEach(task => {
- const taskElement = document.createElement('div');
- taskElement.className = `task-item ${task.completed ? 'completed' : ''}`;
- taskElement.innerHTML = `
- <div class="task-header">
- <h3>${this.escapeHtml(task.title)}</h3>
- <div class="task-actions">
- <button class="toggle-task" data-id="${task.id}">
- ${task.completed ? 'Mark Incomplete' : 'Mark Complete'}
- </button>
- <button class="delete-task" data-id="${task.id}">Delete</button>
- </div>
- </div>
- <p>${this.escapeHtml(task.description)}</p>
- <div class="task-meta">
- <small>Created: ${task.createdAt.toLocaleString()}</small>
- ${task.updatedAt > task.createdAt ? `<small>Updated: ${task.updatedAt.toLocaleString()}</small>` : ''}
- </div>
- `;
-
- this.taskList.appendChild(taskElement);
- });
-
- // Add event listeners to buttons
- document.querySelectorAll('.toggle-task').forEach(button => {
- button.addEventListener('click', (e) => {
- const id = parseInt((e.target as HTMLElement).getAttribute('data-id')!);
- this.handleToggleTask(id);
- });
- });
-
- document.querySelectorAll('.delete-task').forEach(button => {
- button.addEventListener('click', (e) => {
- const id = parseInt((e.target as HTMLElement).getAttribute('data-id')!);
- this.handleDeleteTask(id);
- });
- });
- }
- private async handleToggleTask(id: number): Promise<void> {
- try {
- const task = await this.taskManager.getTask(id);
- if (task) {
- await this.taskManager.updateTask(id, { completed: !task.completed });
- this.loadTasks();
- }
- } catch (error) {
- console.error('Error toggling task:', error);
- }
- }
- private async handleDeleteTask(id: number): Promise<void> {
- try {
- await this.taskManager.removeTask(id);
- this.loadTasks();
- } catch (error) {
- console.error('Error deleting task:', error);
- }
- }
- private escapeHtml(text: string): string {
- const div = document.createElement('div');
- div.textContent = text;
- return div.innerHTML;
- }
- }
- // 5. 初始化应用
- document.addEventListener('DOMContentLoaded', () => {
- const container = document.getElementById('app')!;
- const taskService = new LocalStorageTaskService();
- const taskManager = new TaskManager(taskService);
- const uiController = new TaskUIController(taskManager, container);
- });
复制代码
6.2 常见问题与解决方案
在使用TypeScript的过程中,你可能会遇到一些常见问题。以下是一些解决方案:
- // 问题代码
- const user = JSON.parse('{"name": "John", "age": 30}');
- console.log(user.name); // 错误: 类型"{}"上不存在属性"name"
- // 解决方案1: 类型断言
- const user = JSON.parse('{"name": "John", "age": 30}') as { name: string; age: number };
- console.log(user.name);
- // 解决方案2: 接口定义
- interface User {
- name: string;
- age: number;
- }
- const user = JSON.parse('{"name": "John", "age": 30}') as User;
- console.log(user.name);
- // 解决方案3: 类型守卫
- function isUser(obj: any): obj is User {
- return typeof obj === 'object' &&
- obj !== null &&
- typeof obj.name === 'string' &&
- typeof obj.age === 'number';
- }
- const user = JSON.parse('{"name": "John", "age": 30}');
- if (isUser(user)) {
- console.log(user.name);
- }
复制代码- // 问题代码
- interface User {
- name: string;
- email?: string;
- }
- function getEmail(user: User): string {
- return user.email.toLowerCase(); // 错误: 对象可能为"undefined"
- }
- // 解决方案1: 使用可选链操作符
- function getEmail(user: User): string {
- return user.email?.toLowerCase() || 'No email provided';
- }
- // 解决方案2: 使用类型守卫
- function getEmail(user: User): string {
- if (user.email) {
- return user.email.toLowerCase();
- }
- return 'No email provided';
- }
- // 解决方案3: 使用默认值
- function getEmail(user: User): string {
- const email = user.email || 'No email provided';
- return email.toLowerCase();
- }
复制代码- // 问题代码
- function fetchData() {
- return fetch('/api/data')
- .then(response => response.json())
- .then(data => {
- return data.items; // 错误: 类型"{}"上不存在属性"items"
- });
- }
- // 解决方案1: 定义返回类型
- interface DataItem {
- id: number;
- name: string;
- }
- interface ApiResponse {
- items: DataItem[];
- }
- async function fetchData(): Promise<DataItem[]> {
- const response = await fetch('/api/data');
- const data = (await response.json()) as ApiResponse;
- return data.items;
- }
- // 解决方案2: 使用泛型
- async function fetchData<T>(): Promise<T> {
- const response = await fetch('/api/data');
- return response.json() as Promise<T>;
- }
- // 使用示例
- interface DataItem {
- id: number;
- name: string;
- }
- interface ApiResponse {
- items: DataItem[];
- }
- async function getData() {
- const response = await fetchData<ApiResponse>();
- return response.items;
- }
复制代码- // 问题代码
- function getProperty(obj: any, key: string) {
- return obj[key]; // 返回类型为any
- }
- // 解决方案1: 使用泛型和keyof
- function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
- return obj[key];
- }
- // 使用示例
- interface User {
- name: string;
- age: number;
- }
- const user: User = { name: 'John', age: 30 };
- const userName = getProperty(user, 'name'); // 类型为string
- const userAge = getProperty(user, 'age'); // 类型为number
- const invalid = getProperty(user, 'email'); // 错误: 类型"email"不存在于类型"User"中
- // 解决方案2: 使用索引签名
- interface StringMap {
- [key: string]: string;
- }
- function getValues(map: StringMap, keys: string[]): string[] {
- return keys.map(key => map[key]);
- }
复制代码- // 问题代码
- import _ from 'lodash';
- const result = _.map([1, 2, 3], n => n * 2); // 错误: 找不到模块"lodash"或其相应的类型声明
- // 解决方案1: 安装类型定义
- npm install --save-dev @types/lodash
- // 解决方案2: 创建自己的类型声明
- // 在项目中创建types/lodash.d.ts文件
- declare module 'lodash' {
- export function map<T, U>(array: T[], iteratee: (item: T) => U): U[];
- // 添加其他你使用的lodash函数...
- }
- // 解决方案3: 使用require和类型断言
- const _ = require('lodash') as any;
- const result = _.map([1, 2, 3], n => n * 2);
复制代码
总结
TypeScript是一个强大的工具,它通过添加静态类型系统和其他特性来扩展JavaScript。本指南从基础语法开始,逐步介绍了TypeScript的高级特性和实际应用。
通过学习本指南,你已经了解了:
1. TypeScript的基本类型和语法
2. 接口、类和泛型的使用
3. 中级特性如高级类型、类型守卫和装饰器
4. 高级特性如条件类型、映射类型和模板字面量类型
5. 工程实践,包括配置文件、框架集成和最佳实践
6. 实战项目开发
要真正掌握TypeScript,最重要的是实践。尝试将TypeScript应用到你的项目中,逐步采用其特性,你会发现代码的可维护性和可靠性会显著提高。
TypeScript的生态系统不断发展,新的特性和改进不断推出。保持学习,关注TypeScript的最新发展,将帮助你充分利用这个强大的工具。
版权声明
1、转载或引用本网站内容(TypeScript入门指南从基础语法到高级特性的完整学习路径)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://pixtech.cc/thread-39934-1-1.html
|
|