TypeScript学习

137 阅读26分钟

环境搭建

#全局库 为了全局可以执行tsc
npm i typescript -g
#配置文件
tsc --init
#生成paackge.json
npm init -y

index.ts

const msg = 'xxxx'

function sayhello(msg:String) {
    return "xxx"+msg
}
console.log(sayhello("gooooo"))

编译ts文件

tsc index.ts

生成index.js

var msg = 'xxxx';
function sayhello(msg) {
    return "xxx" + msg;
}
console.log(sayhello("gooooo"));

再执行js

node index.js 

也可以直接使用bun或ts-node

# bun
npm install -g bun
bun index.ts

#ts-node
npm install -g ts-node 
ts-node index.ts

类型

常见boolean/number/string/Array

  • boolean
  • number 可以是int 或是浮点
  • string
  • Array 或 []
// ts-test.ts
let var1: string; // 类型注解
var1 = "xxxx"; // 正确
var1 = 4; // 错误
// 编译器类型推断可省略这个语法
let var2 = true;

// 常见原始类型: string,number,boolean,undefined,null,symbol
// 类型数组
let arr: string[];
arr = ['Tom']; // 或Array<string>

// 任意类型any
let varAny: any;
varAny = 'xx';
varAny = 3;

// any类型也可用于数组
let arrAny: any[];
arrAny = [1, true, "free"];
arrAny[1] = 100;

// 函数中的类型约束
function greet(person: string): string {
return 'hello, ' + person;
}

// void类型,常用于没有返回值的函数
function warn(): void {}

Symbol

const a = Symbol();
let obj = {
  [a]: "111",
};

console.log(obj[a]); // 111 

Enum

通用枚举

enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}
// 生成代码支持两种访问方式  
let dir: Direction = Direction.WEST;
let dir2: Direction = Direction[0];

console.log(dir)
console.log(dir2)

字符串枚举

enum Direction {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST",
}
// 字符串定义 只能用key访问,不能用索引
let dir: Direction = Direction.SOUTH;
console.log(dir)

常量枚举 ,保存的是number

const enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}
 
let dir: Direction = Direction.SOUTH;
console.log(dir) //1

any

支持所有类型,支持赋值,支持方法调用

let test: any ;
test = 'x'//ok
test = false//ok
test() //ok
test.xx //ok

unknow

unknow支持被任何类型值型值,但是unknow 只能赋值给 any 或 unknow

let test: unknow ;//ok
test = 1//ok
test = 'xxx'//ok
test = true //ok

let a:any = test; //ok
let b:unknow = test; //ok
let c:number = test; //报错
let d:string = test; //报错
let e:boolean = test; //报错

//如果没有类型断言或基于控制流的类型细化,则不能在它上面进行任何操作
let value4: unknown;
value4 += 1; // error 对象的类型为 "unknown"

// unknown 与任何其它类型组成的交叉类型,最后都等于其它类型:
type type1 = unknown & string; // type1 => string
type type2 = number & unknown; // type2 => number
type type3 = unknown & unknown; // type3 => unknown
type type4 = unknown & string[]; // type4 => string[]

// unknown 与任何其它类型组成的联合类型,都等于 与 unknown 类型,但只有 类 any例外
type type5 = string | unknown; // type5 => unknown
type type6 = any | unknown; // type6 => any
type type7 = number[] | unknown; // type7 => unknown

//never 类型是 类 unknown 的子类型:
type type8 = never extends unknown ? true : false; // type8 => true

// keyof unknown 等于类型 等 never:
type type9 = keyof unknown; // type9 => never

//只能对 只 unknown 进行等或不等操作,不能进行其它操作
value1 === value2;
value1 !== value2;
value1 += value2; // error

//unknown 类型的值不能访问其属性、作为函数调用和作为类创建实例:
let value5: unknown;
value5.age; // error
value5(); // error
new value5(); // error

// 使 unknown 类型,则不会映射任何属性:
type Types<T> = { [P in keyof T]: number };
type type10 = Types<any>; // type10 => { [x: string]: number }
type type11 = Types<unknown>; // type10 => {}

与any的区别

  • any:可以做任何操作
  • unknown 不允许在没有类型断言或基于控制流的类型细化时对unknown 类型的值做任何操作

Tuple元祖

包含不同类型的数组

let a:[string,number] = ['x',2]

//可以通过 length约束长度
interface Tuple extends Array<number | string> {
0: string;
1: number;
length: 2;
}

Void

代表没有任何类型,和any是相反

function func1(params:string): void {}
let a : void = undefined //void 只能是undefined 或 null

undefined

let u: undefined = undefined;

null

let u: null = null;

配置 strictNullChecks

"strictNullChecks":true,

可选参数会被自动加上 |undefined

const sum = (x: number, y?: number) => {
  return x + (y || 0);
};
sum(1, 2); // 3
sum(1); // 1
sum(1, undefined); // 1
sum(1, null); // error Argument of type 'null' is not assignable to parameter of type 'number | undefined'

对象

  • object (小写o ): 除了js的8个原始数据类型(包含undefined null),才能赋值给它
  • Object (大写O ): 原始数据类型,和非原始数据类型都可以 ,但是严格下 undefined null不支持
  • {}: 与上面Object (大写O )等价
let a:object = Number //ok
let b:object = 1 //error
let c:object = null //error
let a: Object;
a = 1; // ok
a = 'a'; // ok
a = true; // ok
a = null; // 报错
let a: {};
a = 1; // ok
a = 'a'; // ok
a = true; // ok
a = null; // 报错

never

永远无法到达:异常 或 死循环

// 异常
function err(msg: string): never { // OK
  throw new Error(msg); 
}

// 死循环
function loopForever(): never { // OK
  while (true) {};
}

let a: never;
let nev: never;
let an: any;

a = 444; // Error
a = nev; // OK
a = an; // Error
a = (() => { throw new Error("异常"); })(); // OK
a = (() => { while(true) {} })(); // OK

用于确保 类型全面性检查

type Foo = string | number; //防止 type Foo = string | number | boolean; 加入新类型漏写逻辑
function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    const check: never = foo;
  }
}

接口

接口仅约束结构,不要求实现,使用更简单

interface Person {
    firstName: string;
    lastName: string;
}
function greeting(person: Person) {
    return 'Hello, ' + person.firstName + ' ' + person.lastName;
}
greeting({ firstName: 'Jane', lastName: 'User' }); // 正确
greeting({ firstName: 'Jane' }); // 错误

// 接口中只需定义结构,不需要初始化
export interface Feature {
    id: number;
    name: string;
}

定义方法

type Ioperator = 'plus' | 'minus'
interface ICalculator{
    (Ioperator: Ioperator, num1: number, num2: number): number,// 定义为函数,调用签名
    plus: (num1: number, num2: number)=> number; //对象方法
    minus: (num1: number, num2: number)=> number;//对象方法
}
declare const calculator: ICalculator

calculator 即是函数也是对象

  calculator('plus',1,2)
  calculator.plus(1,2)

可选 | 只读属性

readonly 和 ReadonlyArray

interface Obj {
  readonly a: string;
  b?: number;
}
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error! 

任意属性

interface Obj {
  name: string;
  age?: number;
  [propName: string]: any; //允许任意新类型
}

const p1:Obj = { name: "11" };
const p2:Obj = { name: "22", age: 5 };
const p3:Obj = { name: "33", sex: 1 }

索引类型

interface RoleDic {
  [id: number]: string;
}
const role1: RoleDic = {
  0: "super_admin",
  1: "admin",
};
const role2: RoleDic = {
  s: "super_admin", // error 不能将类型"{ s: string; a: string; }"分配给类型"RoleDic"。
  a: "admin",
};
const role3: RoleDic = ["super_admin", "admin"];

typeof

获取当前类型

interface Person {
  name: string;
  age: number;
}

const a: Person = { name: '111', age: 11 };
type Person1 = typeof a; // -> Person

索引访问操作符

interface Info {
  name: string;
  age: number;
}
type NameType = Info["name"];
let name: NameType = 123; // error 不能将类型“123”分配给类型“string”

别名

使用类型别名自定义类型,其实定义的是多个类型的结构,等价于interface ,interface是更早的产物,兼容更好,是一个引用对象。

// 可以用下面这样方式定义对象类型 
const objType: { foo: string, bar: string }
// 使用type定义类型别名,使用更便捷,还能复用 
type Foobar = { foo: string, bar: string }
const aliasType: Foobar

字面量类型(字符串和数字)

type Name = "Lison";
const name1: Name = "test"; // error 不能将类型“"test"”分配给类型“"Lison"”
const name2: Name = "Lison";

type Age = 18;
interface Info {
  name: string;
  age: Age;
}
const info: Info = {
  name: "Lison",
  age: 28, // error 不能将类型“28”分配给类型“18”
};

联合类型

希望某个变量或参数的类型是多种类型其中之一

let union: string | number;
union = '1'; // ok 
union = 1; // ok

交叉类型

//想要定义某种由多种类型合并而成的类型使用交叉类型
type First = { first: number };
type Second = { second: number };
// FirstAndSecond将同时拥有属性first和second 
type FirstAndSecond = First & Second;

//范例:利用交叉类型给Feature添加一个selected属性
type Select = {
    selected: boolean
}
export type FeatureSelect = Feature & Select

features: FeatureSelect[] = [
    { id: 1, name: "类型注解", selected: false },
    { id: 2, name: "编译型语言", selected: true }
];

别名与接口区别

别名能声明 原始类型、联合类型和元组

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string]; 

继承差异

interface 用 extends type 用 &

interface 多次定义自动合并

interface a { x: number; }
interface a { y: number; }

const point: a = { x: 1, y: 2 };

可以多继承

interface Vegetables {
  color: string;
}
interface Food {
  type: string;
}
interface Tomato extends Food, Vegetables {
  radius: number;
}
const tomato: Tomato = {
  type: "vegetables",
  color: "red",
  radius: 1.2,
}; 

typeof

获取当前类型

function sum(x: number): Array<number> {
  return [x];
}

type Func = typeof sum; // -> (x: number) => number[]

函数

必填参:参数一旦声明,就要求传递,且类型需符合
// 02-function.ts 
function greeting(person: string): string {
    return "Hello, " + person;
}
greeting('tom')
//可选参数:参数名后面加上问号,变成可选参数
function greeting(person: string, msg?: string): string {
return "Hello, " + person;
} 

//默认值
function greeting(person: string, msg = ''): string {
return "Hello, " + person;
}

接口定义函数

interface Counter {
  (): void; // 这里定义Counter这个结构必须包含一个函数,函数的要求是无参数,返回值为void,即无返回值
  count: number; // 而且这个结构还必须包含一个名为count、值的类型为number类型的属性
}
const getCounter = (): Counter => {
  const c = () => {
    // 定义一个函数,逻辑和前面例子的一样
    c.count++;
  };
  c.count = 0; // 再给这个函数添加一个count属性初始值为0
  return c; // 最后返回这个函数对象
};
const counter: Counter = getCounter(); // 通过getCounter函数得到这个计数器
counter();
console.log(counter.count); // 1
counter();
console.log(counter.count); // 2

函数重载

  • 以参数数量或类型区分多个同名函数
  • 声明不同的参数与返回值
  • 最后一个实现逻辑
function handleData(x: string): string[]; // 这个是重载的一部分,指定当参数类型为string时,返回值为string类型的元素构成的数组
function handleData(x: number): string; // 这个也是重载的一部分,指定当参数类型为number时,返回值类型为string
function handleData(x: any): any {
  // 这个就是重载的内容了,他是实体函数,不算做重载的部分
  if (typeof x === "string") {
    return x.split("");
  } else {
    return x.toString().split("").join("_");
  }
}
handleData("abc").join("_");
handleData(123).join("_"); // error 类型"string"上不存在属性"join"
handleData(false); // error 类型"boolean"的参数不能赋给类型"number"的参数。

可选参数 默认参数 剩余参数

// 可选参数
function createUserId(name: string, id: number, age?: number): string {
  return name + id;
}

// 默认参数
function createUserId(
  name: string = "semlinker",
  id: number,
  age?: number
): string {
  return name + id;
}

class的特性 ts中的类和es6中大体相同,这里重点关注ts带来的访问控制等特性

class Parent {
    private _foo = "foo"; // 私有属性,不能在类的外部访问
    protected bar = "bar"; // 保护属性,可以在子类中访问
    // 参数属性:构造函数参数加修饰符,能够定义为成员属性
    constructor(public tua = "tua") { }
    //等价于 java的 省略代码
    //constructor(String tua) { 
    //    this.tua = tua
    //}
    // 方法也有修饰符
    private someMethod() { }
    // 存取器:属性方式访问,可添加额外逻辑,控制读写性
    //类似于 vue里面的computed 的计算属性。里面直接使用this
    get foo() {
        return this._foo;
    }
    set foo(val) {
        this._foo = val;
    }
} 

static

静态属性与方法

class Obj { 
  static cname: string = "obj"; 
  b: string; 
  constructor(message: string) {
    this.b = message;
  } 
  static getClassName() {
    return "obj";
  } 
  test() {
    return "Hello, " + this.b;
  }
}

let obj = new Obj("aaaa");

抽象方法

abstract class Person {
  constructor(public name: string){}

  abstract say(words: string) :void;
}
 
const lolo = new Person(); // Error 必须要有类继承实现 

实现Person

class Person2 extends Person {
  constructor(name: string) {
    super(name);
  }
  
  say(words: string): void {
    console.log(`${this.name} says ${words}`);
  }
}
 

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定 类型的一种特性。以此增加代码通用性。

// 不用泛型
// interface Result {
// ok: 0 | 1;
// data: Feature[];
// }
// 使用泛型
interface Result<T> {
    ok: 0 | 1;
    data: T;
}
// 泛型方法
function getResult<T>(data: T): Result<T> {
    return { ok: 1, data };
}
// 用尖括号方式指定T为string
getResult < string > ('hello')
// 用类型推断指定T为number
getResult(1)

泛型优点:

  • 函数和类可以支持多种类型,更加通用
  • 不必编写多条重载,冗长联合类型,可读性好
  • 灵活控制类型约束
  • 不仅通用且能灵活控制,泛型被广泛用于通用库的编写。

范例:用axios获取数据

//安装axios: npm i axios - S
//配置一个模拟接口,vue.config.js
//使用接口,HelloWorld.vue
module.exports = {
    devServer: {
        before(app) {
            app.get('/api/list', (req, res) => {
                res.json([
                    { id: 1, name: "类型注解", selected: false },
                    { id: 2, name: "编译型语言", selected: true }
                ])
            })
        }
    }
} 
async mounted() {
    console.log("HelloWorld");
    const resp = await axios.get < FeatureSelect[] > ('/api/list')
    this.features = resp.data
}

keyof

注意: keyof 拿到的是具体的key内容。 如下面的name 和 age

interface Obj {
  name: string;
  age: number;
}

type K1 = keyof Obj; // "name" | "age"
type K2 = keyof Obj[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"  
type K3 = keyof {[x:string]:Obj};  // string | number //传入的是任意的 key ,就变成对应的key类型, 对象中key只有 number 或string
let a:K3 = 'string'


interface Info {
  name: string;
  age: number;
}
let key2: keyof Info; // keys的类型为number
key2 = 'age'; 

//索引特殊情况
interface Obj<T> {
  [key: number]: T;
}
let key: keyof Obj<string>; // keys的类型为number
key = 'xx'; // 报错,这里只能是 number 类型


interface Obj<T> {
  [key: string]: T;
}
const obj: Obj<number> = {
  age: 18,
};
let value: Obj<number>["age"]; // value的类型是number,也就是name的属性值18的类型
value = 'xx' // 报错 只能是number类型


in

type Keys = "a" | "b" | "c"
type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }

extends

泛型约束

function printLength<T extends { length: number }>(obj: T) {
  console.log(obj.length);
}

printLength("Hello"); // 字符串有 length 属性
printLength([1, 2, 3]); //  数组有 length 属性 
// printLength(123); //  Error: 类型“number”没有 length 属性


const getProp = <T, K extends keyof T>(object: T, propName: K) => {
  return object[propName];
};
const obj = { a: "aa", b: "bb" };
getProp(obj, "c"); // 类型“"c"”的参数不能赋给类型“"a" | "b"”的参数

注意: K extends keyof T 中 K拿到的是 对应具体的key值,如上面的 a 或 b

提取对象属性

type PropertyType<T, K extends keyof T> = T[K];

type User = { id: number; name: string };

type A = PropertyType<User, "id">;   // number
type B = PropertyType<User, "name">; // string

注意: T[K]拿到的是 key对应的类型,如上面 id对应的number类型, name对应的striing 获取类型值

type IsString<T> = T extends string ? "Yes" : "No";
type A = IsString<string>;  // "Yes"
type B = IsString<number>;  // "No"

条件

T extends U ? X : Y 理解它是三元操作符的形式

  • T extends U是判断条件
  • 如果T的类型符合U,则取类型X,否则为类型Y。
// 如果 T 可以赋值给 U 类型,则是 X 类型,否则是 Y 类型
T extends U ? X : Y

// 例子
type Type<T> = T extends string ? string : number
let index:Type<'dd'> // index的类型为string
let index2: Type<33> // index2的类型为number

分布式条件类型

当待检测的类型是联合类型,则该条件类型被称为“分布式条件类型”,在实例化时会自动分发成联合类型


type TypeName<T> = T extends any ? T : never;
type Type1 = TypeName<string | number>; // Type1的类型是string|number


// 找出从 T 中出去 U 中存在的类型,得到剩下的类型 对应系统自带的Exclude
type Diff<T, U> = T extends U ? never : T;
type Test = Diff<string | number | boolean, undefined | number>;
// Test的类型为string | boolean
type Type<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
interface Part {
  id: number;
  name: string;
  subparts: Part[];
  updatePart(newName: string): void;
}
type Test = Type<Part>; // Test的类型为"updatePart"

infer 条件类型的类型推断

一般配合 extends 使用,可以理解为方程式里面的 x 的求值占位符。

type Type<T> = T extends any[] ? T[number] : T; // T[number]为数组每个的元素类型 
type test = Type<symbol[]>; // test的类型为 symbol
type test2 = Type<string>; // test2的类型为 string

使用 infer U 代替 any[]T[number] ,暂位符

type Type<T> = T extends Array<infer U> ? U : T;
type test = Type<string[]>; // test的类型为string
type test2 = Type<string>; // test2的类型为string

//这里infer R 相当于提前确认被继承的类型,再反向推到,并且保存在R 并且条件满足就返回
type ReturnTypeInfer<T> = T extends (...args: any[]) => infer R ? R : never;

// 示例函数
function getUser() {
  return { name: "Alice", age: 25 };
}

// 使用推导
type UserType = ReturnTypeInfer<typeof getUser>;
// UserType 结果为 { name: string; age: number }

提取数组类型

type ArrayElement<T> = T extends (infer U)[] ? U : never;

// 示例
type StringArray = ArrayElement<string[]>; // StringArray 为 string
type NumberArray = ArrayElement<number[]>; // NumberArray 为 number

提取promise类型

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// 示例
type ResponseType = UnwrapPromise<Promise<number>>; // ResponseType 为 number

Partial

让当前的对象属性变成可选

type Partial<T> = {
  [P in keyof T]?: T[P];  // 等价[P in keyof T]+?: T[P];  使用+? 效果是一样的
};

interface UserInfo {
    idstring;
    namestring;
}
// error:Property 'id' is missing in type '{ name: string; }' but required in type 'UserInfo'
const xiaomingUserInfo = {
    name'xiaoming'
}

type NewUserInfo = Partial<UserInfo>;
const xiaomingNewUserInfo = {
    name'xiaoming'
}

Required & Readonly

将类型的属性变成必选 或 只读

type Required<T> = { 
    [P in keyof T]-?: T[P]  // 这里使用了 特殊符号- 来移除
};
// 只读
type Readonly<T> = {
    readonly [p in keyof T]: T[p];
}

Record

把 K 属性 转化为 T的key

type Record<K extends keyof any, T> = {
      [P in K]: T;
};

interface PageInfo {
    title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
    about: { title: "1111" },
    contact: { title: "2222" },
    home: { title: "3333" },
};


//把一个对象中的每一个属性转换为其他值的场景
function mapObject<K extends string | number, T, U>(
  obj: Record<K, T>,
  f: (x: T) => U
): Record<K, U> {
  let res = {} as Record<K, U>;
  for (const key in obj) {
    res[key] = f(obj[key]);
  }
  return res;
}
const names = { 0: "hello", 1: "world", 2: "bye" };
const lengths = mapObject(names, (s) => s.length); // { 0: 5, 1: 5, 2: 3 }

ReturnType

函数的返回值类型

type ReturnType<T extends (...argsany[]) => any> = T extends (
  ...argsany[]
) => infer R
  ? R
  : any;

type Func = (value: number) => string;
const fooReturnType<Func> = "xxxx";

Exclude

去除重复的元素

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

Extract

提取重复

type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a" 

Pick

选一部分属性出来

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

Omit

剔除一个属性

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

NonNullable

去掉 null 及 undefined

type NonNullable<T> = T extendsnull | undefined ? never : T;
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]

Parameters

把参数转成元数组

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any
? P : never;

type A = Parameters<() =>void>; // []
type B = Parameters<typeofArray.isArray>; // [any]
type C = Parameters<typeofparseInt>; // [string, (number | undefined)?]
type D = Parameters<typeofMath.max>; // number[]

InstanceType

获取构造函数类型的实例类型

type InstanceType<T extends new (...args: any[]) => any> = T extends new (
...args: any[]
) => infer R
? R
: any;

// 使用
class A {
constructor() {}
}
type T1 = InstanceType<typeof A>; // T1的类型为A
type T2 = InstanceType<any>; // T2的类型为any
type T3 = InstanceType<never>; // T3的类型为never
type T4 = InstanceType<string>; // error

ThisType

ThisType<T>中的T对应this, 如下面{ value: number }对应this的结构

type Config = {
    data: { value: number };
    increment: () => void;
    reset: () => void;
} & ThisType<{ value: number }>;

const config: Config = {
    data: { value: 0 },
    increment() {
        this.value++; // this 指向 { value: number }
    },
    reset() {
        this.value = 0;
    }
};

元祖或数组的应用

我们定义了一个元组Tuple,元素类型分别为number、string和boolean,使用MapToPromise映射类型将这个元组类型传入

type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> }; // 这里 遍历了所有元祖里面的类型
type Tuple = [number, string, boolean];
type promiseTuple = MapToPromise<Tuple>;
let tuple: promiseTuple = [
  new Promise((resolve, reject) => resolve(1)),
  new Promise((resolve, reject) => resolve("a")),
  new Promise((resolve, reject) => resolve(false)),
];

断言

<xxx>valval as xxx

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

非空 xx!

function myFunc(maybeString: string | undefined | null) { 
    const a: string = maybeString; // Error
    const b: string = maybeString!; // Ok 注意实际运行代码还是有可能为 undefined
  } 
function myFunc(f: Func1 | undefined) { 
  const num1 = f(); // Error
  const num2 = f!(); //OK
}

属性断言

let x!: number; //如果不加!会报错
initialize();
console.log(2 * x); // Ok

function initialize() {
  x = 10;
}

类型守卫

是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内

in

interface Admin {
    name: string;
    privileges1: string[];
  }
  
  interface Employee {
    name: string;
    startDate1: Date;
  }
  
  type UnknownEmployee = Employee | Admin;
  
  function test(emp: UnknownEmployee) {
    console.log("Name: " + emp.name);
    if ("privileges" in emp) {
      console.log("Privileges: " + emp.privileges);
    }
    if ("startDate" in emp) {
      console.log("Start Date: " + emp.startDate);
    }
  }

typeof

type 只能是 number 、 string 、 boolean 和 symbol 四种类型,其他object 、 functio n 和 undefined无效

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

let padder: Padder = new FatherPadder();

if (padder instanceof FatherPadder) {
   
}

is

用于类型守卫(Type Guards),让 TypeScript 进行类型缩小(Type Narrowing)。

function isNumber(x: any): x is number {
  return typeof x === "number";
}

case判断遗漏

interface Square {
  kind: "square";
  size: number;
}
interface Rectangle {
  kind: "rectangle";
  height: number;
  width: number;
}
interface Circle {
  kind: "circle";
  radius: number;
}
interface Triangle {
  kind: "triangle";
  bottom: number;
  height: number;
}
type Shape = Square | Rectangle | Circle | Triangle; // 这里我们在联合类型中新增了一个接口,但是下面的case却没有处理Triangle的情况

function assertNever(value: never): never {
  throw new Error("Unexpected object: " + value);
}
function getArea(s: Shape): number {
  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;
    default:
      return assertNever(s); // error 类型“Triangle”的参数不能赋给类型“never”的参数
  }
}

装饰器

装饰器是一种新的声明,它能够作用于类声明、方法、访问符、属性和参数上

装饰器定义

function setProp (target) {
// ...
}
@setProp

装饰器工厂

也是一个函数,它的返回值是一个函数

function setProp () {
return function (target) {
// ...
}
}
@setProp()

装饰器组合

// 可以写在一行
@setName @setAge target
// 可以换行
@setName @setAge
target
  • 装饰器工厂从上到下依次执行,但是只是用于返回函数但不调用函数;
  • 装饰器函数从下到上依次执行,也就是执行工厂函数返回的函数。
function setName() {
  console.log("get setName");
  return function (target) {
    console.log("setName");
  };
}
function setAge() {
  console.log("get setAge");
  return function (target) {
    console.log("setAge");
  };
}
@setName()
@setAge()
class Test {}
// 打印出来的内容如下:
/**
  'get setName'
  'get setAge'
  'setAge'
  'setName'
  */

装饰器求值

类的定义中不同声明上的装饰器将按以下规定的顺序引用:

  1. 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个实例成员;
  2. 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个静态成员;
  3. 参数装饰器应用到构造函数;
  4. 类装饰器应用到类

类装饰器

类装饰器表达式会在运行时当做函数被调用,它由唯一一个参数,就是装饰的这个类。

//类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
function log(target: Function) {
    // target是构造函数
    console.log(target === Foo); // true
    target.prototype.log = function () {
        console.log(this.bar);
    }
}
@log
class Foo {
    bar = 'bar'
}
const foo = new Foo(); 
foo.log();

例子2

let sign = null;
function setName(name: string) {
  return function (target: Function) {
    sign = target;
    console.log(target.name);
  };
}
@setName("lison") // Info
class Info {
  constructor() {}
}
console.log(sign === Info); // true
console.log(sign === Info.prototype.constructor); // true

通过装饰器,我们就可以修改类的原型对象和构造函数

function addName(constructor: { new (): any }) {
  constructor.prototype.name = "lison";
}
@addName
class A {}
const a = new A();
console.log(a.name); // error 类型“A”上不存在属性“name”

通过声明合并解决这个问题

function addName(constructor: { new (): any }) {
  constructor.prototype.name = "lison";
}
@addName
class A {}
interface A {
  name: string;
}
const a = new A();
console.log(a.name); // "lison"

官方例子

// 类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function classDecorator<T extends { new (...args: any[]): {} }>(target: T) {
  return class extends target {
    newProperty = "new property";
    hello = "override";
  };
}
@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}
console.log(new Greeter("world"));
/*
  {
  hello: "override"
  newProperty: "new property"
  property: "property"
  }
  */



// 改进 装饰器的返回值还是返回一个类,但是这个类不继承被修饰的类了,
// 所以最后打印出来的实例,只包含装饰器中返回的类定义的实例属性,被装饰的类的定义被替换了。
function classDecorator(target: any): any {
  return class {
    newProperty = "new property";
    hello = "override";
  };
}
@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}
console.log(new Greeter("world"));
/*
  {
  hello: "override"
  newProperty: "new property"
  }
  */


方法装饰器

3个参数

  • 装饰静态成员时是类的构造函数,装饰实例成员时是类的原型对象;
  • 成员的名字;
  • 成员的属性描述符。

例子1

function rec(target: any, name: string, descriptor: any) {
    // 这里通过修改descriptor.value扩展了bar方法
    //注意这里是ts指定的格式,只能通过descriptor.value 扩展该方法,
    const baz = descriptor.value;//这里是保留原来的引用,下面再进行覆盖
    descriptor.value = function (val: string) {
        console.log('run method', name);
        baz.call(this, val);
    }
}
class Foo {
    @rec
    setBar(val: string) {
        this.bar = val
    }
}
const foo = new Foo();
foo.setBar('lalala') 
复习Object.defineProperty

configurable, writable , enumerable

var obj = {};
Object.defineProperty(obj, "name", {
  value: "lison",
  writable: false,
  configurable: true,
  enumerable: true,
});
console.log(obj);
// { name: 'lison' }
obj.name = "test";
console.log(obj);
// { name: 'lison' }
for (let key in obj) {
  console.log(key);
}
// 'name'
Object.defineProperty(obj, "name", {
  enumerable: false,
});
for (let key in obj) {
  console.log(key);
}
// 什么都没打印
Object.defineProperty(obj, "name", {
  writable: true,
});
obj.name = "test";
console.log(obj);
// { name: 'test' }
Object.defineProperty(obj, "name", {
  configurable: false,
});
Object.defineProperty(obj, "name", { // error Cannot redefine property: name 
  writable: false,
});

应用
function enumerable(bool: boolean) {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor
  ) {
    console.log(target); // { getAge: f, constructor: f }
    descriptor.enumerable = bool; // 设置为false后 就不能被for in 循环
  };
}
class Info {
  constructor(public age: number) {}
  @enumerable(false)
  getAge() {
    return this.age;
  }
}
const info = new Info(18);
console.log(info);
// { age: 18 } // 这里不会打印getAge()方法
for (let propertyName in info) {
  console.log(propertyName);
}
// "age"   // 这里不会打印getAge()方法

function () { return “not age” } 替换了被装饰的方法 getAge () { return this.age }

function enumerable(bool: boolean): any {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor
  ) {
    return {
      value: function () {
        return "not age";
      },
      enumerable: bool,
    };
  };
}
class Info {
  constructor(public age: number) {}
  @enumerable(false)
  getAge() {
    return this.age;
  }
}
const info = new Info(100);
console.log(info.getAge()); //not age, 这里原来应该打印 100 但是被替换了 变成 not age
js实现

实现1

class Log {
    print(msg) {
        console.log(msg)
    }
}
const dec = (target,property) => {
    const old = target.prototype[property]
    target.prototype[property] = (msg) => {
        //这里可以写入AOP切面 代码
        msg = `[${msg}]`
        old(msg)
    }
}
dec(Log,'print')
let log = new Log()
log.print('xxx')

实现2 带参数

class Log {
    print(msg) {
        console.log(msg)
    }
}
const dec = (sender) => (target,property) => {
    const old = target.prototype[property]
    target.prototype[property] = (msg) => {
        //这里可以写入AOP切面 代码
        msg = `${sender}说:[${msg}]`
        old(msg)
    }
}
dec('jason')(Log,'print')
let log = new Log()
log.print('xxx')
//结果:jason说:xxx

访问器装饰器

类中的访问器set 和 get 方法,set设置属性值的时候触发,get获取属性值的时候触发

当 enumerable(true) 对象中的name会被打印,同时_name也会被打印

  • 注意 不能向多个同名的 get/set 访问器应用修饰器
function enumerable(bool: boolean) {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.enumerable = bool;
  };
}
class Info {
  private _name: string;
  constructor(name: string) {
    this._name = name;
  }
  @enumerable(true)
  get name() {
    return this._name;
  }
 // @enumerable(false) // error 不能向多个同名的 get/set 访问器应用修饰器
  set name(name) {
    this._name = name;
  }
}
const  info = new Info("zhangsan");
for (let key in info) {
  console.log(key);
}
// 打印
//_name
//name

//当 enumerable(false)
  @enumerable(false)
  get name() {
    return this._name;
  }
//则只打印  
//_name

当 enumerable(false) 对象中的name不会被打印

function enumerable(bool: boolean) {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.enumerable = bool;
  };
}
class Info {
  private _name: string;
  constructor(name: string) {
    this._name = name;
  }
  @enumerable(false)
  get name() {
    return this._name;
  }
 // @enumerable(false) // error 不能向多个同名的 get/set 访问器应用修饰器
  set name(name) {
    this._name = name;
  }
}
const  info = new Info("zhangsan");
for (let key in info) {
  console.log(key);
}


//当 enumerable(false) 
//则只打印  
//_name

属性装饰器

属性装饰器声明在属性声明之前,它有 2 个参数,和方法装饰器的前两个参数是一模一样的。

function printPropertyName(target: any, propertyName: string) {
  console.log(propertyName);
}
class Info {
  @printPropertyName
  name: string;
  @printPropertyName
  age: number;
}
//打印
//name
//age

默认只会被调用一次

function init(target, name) {
    console.log("target",target)
    console.log("target",name)
    target[name] = 'init val'
} 
class Foo {
    @init val1!: string;
}
const foo = new Foo(); 
console.log(foo.val1);
console.log(foo.val1);
foo.val1 = 'init new'
console.log(foo.val1);

// 打印
// target Foo {}
// target val1
// init val
// init val
// init new

新增 默认初始化参数,在原有函数再包装多一层

function init(val : string) {
    return function (target, name) {
        console.log("target",target)
        console.log("target",name)
        target[name] = val
    } 
}
class Foo {
    @init('val1') val1!: string;
}
const foo = new Foo(); 
console.log(foo.val1);
console.log(foo.val1);
foo.val1 = 'init new'
console.log(foo.val1);

// 打印
// target Foo {}
// target val1
// init val1
// init val1
// init new

参数装饰器

function required(target: any, propertName: string, index: number) { //index从0开始计算
  console.log(`修饰的是${propertName}的第${index + 1}个参数`);
}
class Info {
  name: string = "lison";
  age: number = 18;
  getInfo(prefix: string, @required infoType: string): any {
    return prefix + " " + this[infoType];
  }
}
interface Info {
  [key: string]: string | number | Function;
}
const info = new Info();
info.getInfo("hihi", "age"); //打印: 修饰的是getInfo的第2个参数

装饰器是工厂函数,它能访问和修改装饰目标。

其他

模块/命名空间

定义

  • 内部模块 -> 命名空间
  • 外部模块 -> 模块(跟es6标准接近)

差异:

  • 命名空间:程序内部用于防止全局污染,把相关的内容放一起
  • 封装了一个工具或库,用于模块系统中引入使用时

命名空间

命名空间的定义实际相当于定义了一个大的对象

定义

validation.ts

namespace Validation {
  const isLetterReg = /^[A-Za-z]+$/; // 这里定义一个正则
  export const isNumberReg = /^[0-9]+$/; // 这里再定义一个正则,与isLetterReg的区别在于他使用export导出了
  export const checkLetter = (text: any) => {
    return isLetterReg.test(text);
  };
} 
使用

使用/// <reference path="validation.ts"/> 引入

index.ts

/// <reference path="validation.ts"/>
let isLetter = Validation.checkLetter("sdfsd");
const reg = Validation.isNumberReg;
console.log(isLetter);
console.log(reg);

最终生成js,会被合并成一个js内容

var Validation;
(function (Validation) {
  var isLetterReg = /^[A-Za-z]+$/;
  Validation.isNumberReg = /^[0-9]+$/;
  Validation.checkLetter = function (text) {
    return isLetterReg.test(text);
  };
})(Validation || (Validation = {}));
/// <reference path="namespace.ts"/>
var isLetter = Validation.checkLetter("sdfsd");
var reg = Validation.isNumberReg;
console.log(isLetter);
console.log(reg); 

当然也可以使用 export 和 import方式,但不推荐,因为这是模块做的事情

//导出
export namespace Validation {
   ...
}

//导入
import { Validation } from "./Validation.ts";
...

引入多个,会按顺序执行

// a.ts
namespace a {
  ...
}
// b.ts
namespace b {
...
}


// index.ts
/// <reference path="./a.js"/>
/// <reference path="./b.js"/>
...
别名 import

通过import 重新定义一个别名,把多层掐套的关键简化而已

namespace Shapes {
  export namespace Polygons {
    export class Triangle {}
    export class Squaire {}
  }
}
import polygons = Shapes.Polygons; // 这里只是定义一个别名,并不是真正的导入
let sq = new polygons.Squaire();

模块

export
// funcInterface.ts
export interface Func {
  (arg: number): string;
}
export class C {
  constructor() {}
}
class B {}
export { B };
export { B as ClassB };


// main.ts
export * from "./moduleB";
// main.ts
export { name } from "./moduleB";
// main.ts
export { name as nameProp } from "./moduleB";

export default

导出默认

// moduleB.ts
export default "lison";

// main.ts
import name from "./moduleB.ts";
console.log(name); // 'lison'
import
// main.ts
import { name } from "./moduleB";
// main.ts
import * as info from "./moduleB";
//main.ts
import { name as nameProp } from "./moduleB";


//直接导入
import "./set-title.ts";
兼容写法export = 和 import = require()
// moduleC.ts
class C {}
export = C;
// main.ts
import ClassC = require("./moduleC");
const c = new ClassC();

库的类型

全局库

  • 类似于cdn 直接用<script>标签引入,
  • 顶级的var或function声明语句,给window添加属性,判断document或window是否存在的判断逻辑

模块化库

  • 无条件地调用require或define方法,commonjs
  • 使用import或export导入和导出内容,赋值给exports或module.exports es6模块系统

UMD库

同时支持全局库和模块化库,通过下面条件判断

  • 判断 typeof define === “function” && define.amd 的逻辑,
  • 判断 typeof module === “object” && module.exports 的逻辑。

插件

全局插件

声明

声明合并

接口合并

interface Info {
  name: string;
}
interface Info {
  age: number;
}
interface Info {
  age: boolean; // error 后续属性声明必须属于同一类型。属性“age”的类型必须为“number”,但此处却为类型“boolean”
}

// 合并后
interface Info {
  name: string;
  age: number;
}

函数覆盖


interface Res {
  getRes(input: string): number;
}
interface Res {
  getRes(input: number): string;
}
const res: Res = {
  getRes: (input: any): any => {
    if (typeof input === "string") return input.length;
    else return String(input);
  },
};
res.getRes("123").length; // error 类型“number”上不存在属性“length”

命名空间合并


namespace Validation {
  export const checkNumber = () => {};
}
namespace Validation {
  export const checkString = () => {};
}

// 合并后
namespace Validation {
  export const checkNumber = () => {};
  export const checkString = () => {};
}

合并的命名空间, 没有export的, 不能互相访问

namespace Validation {
  const numberReg = /^[0-9]+$/;
  export const stringReg = /^[A-Za-z]+$/;
  export const checkString = () => {};
}
namespace Validation {
  export const checkNumber = (value: any) => {
    return numberReg.test(value); // error 找不到名称“numberReg”
  };
}
命名空间分别和类、函数、枚举

命名空间和类

  • 类的定义必须在命名空间前面,合并后变成,一些以命名空间导出内容为静态属性的类
class Validation {
  checkType() {}
}
namespace Validation {
  export const numberReg = /^[0-9]+$/;
  export const stringReg = /^[A-Za-z]+$/;
  export const checkString = () => {};
}
namespace Validation {
  export const checkNumber = (value: any) => {
    return numberReg.test(value);
  };
}
console.log(Validation.prototype); // { checkType: fun () {} }
console.log(Validation.prototype.constructor);
/**
  {
  checkNumber: ...
  checkString: ...
  numberReg: ...
  stringReg: ...
  }
  */

命名空间和函数

函数的定义要在同名命名空间前面,合并后 = 给一个函数设置属性

function countUp() {
  countUp.count++;
}
namespace countUp {
  export let count = 0;
}
countUp();
countUp();
console.log(countUp.count); // 2

命名空间和枚举

只是简单扩展枚举的内容

enum Colors {
  red,
  green,
  blue,
}
namespace Colors {
  export const yellow = 3;
}
console.log(Colors);
/*
  {
  0: "red",
  1: "green",
  2: "blue",
  red: 0,
  green: 1,
  blue: 2,
  yellow: 3
  }
  */

declare 使用第三方库,需要引入声明 ts才能正常识别

声明全局变量

当某个变量是在外部引入的(比如来自 CDN 的 JavaScript 代码),但 TypeScript 并不知道它的存在时,可以使用 declare 进行声明:

declare const myGlobalVar: string;
console.log(myGlobalVar); // TypeScript 不会报错

声明全局函数

declare function myFunction(param: string): void;
myFunction("Hello"); // TypeScript 允许调用,但不检查具体实现

声明外部模块

在使用没有 TypeScript 类型声明的第三方库时,可以用 declare module 来声明模块:

declare module "my-library" {
  export function doSomething(): void;
}

这样就可以在代码中 import { doSomething } from "my-library"; 而不会报错。

声明 TypeScript 命名空间

可以用 declare namespace 声明一个全局命名空间:

declare namespace MyNamespace {
  export function myMethod(): void;
}
MyNamespace.myMethod();

声明类

declare class MyClass {
  constructor(name: string);
  getName(): string;
}

这仅声明了 MyClass 的类型,但不会生成对应的 JavaScript 实现。

声明 .d.ts 类型文件

*.d.ts 类型定义文件中,declare 被广泛使用,比如:

declare module "lodash" {
  export function cloneDeep<T>(value: T): T;
}

这允许 TypeScript 识别 lodash 模块,而不提供具体实现。

第三方库自动加载

  • 库自带index.d.ts
node_modules/axios/index.d.ts
node_modules/axios/index.js
  • 库不自带index.d.ts ,单独安装 如 @types/lodash
node_modules/@types/lodash/index.d.ts

扩展

扩展全局

// 在一个 TypeScript 文件中 (如: `globals.d.ts`)
declare global {
  interface Window {
    myGlobalVar: string;
  }
}

// 在其他地方可以这样使用:
window.myGlobalVar = "Hello, world!";

扩展某个类

// 在一个 TypeScript 文件中 (如: `globals.d.ts`)
declare global {
  type MyGlobalType = {
    name: string;
    age: number;
  };
}

// 在其他地方使用该类型
const person: MyGlobalType = {
  name: "John",
  age: 30
};

vue扩展

// 在一个 TypeScript 文件中 (如: `globals.d.ts`)
declare module 'vue' {
  interface ComponentCustomProperties {
    $myGlobalFunction: () => void;
  }
}

// 使用时可以在 Vue 组件内调用
export default {
  mounted() {
    this.$myGlobalFunction();
  }
};

引入

/// <reference path="..." />

  • 一般直接在tsconfig.json配置"include": ["src/**/*.ts", "common/**/*.d.ts"] 即可
  • 手工引入声明文件,直接在代码内引入
// common/util.d.ts
declare const util: {
  greet(name: string): string;
};

// test.ts
/// <reference path="./common/util.d.ts" />
console.log(util.greet("Hello"));

import type

只是在ts编译时使用类型,不真正导入代码

import type { FormInstance } from 'element-plus';
let form: FormInstance; // ✅ 仅用于类型检查
// console.log(FormInstance); // ❌ 语法报错(类型不能作为值使用)

配置

普通配置

{ 
  "compilerOptions": { 
    "target": "ESNext",    // 目标 JavaScript 版本,ESNext 可支持最新特性
    "module": "ESNext",        // 使用 ES 模块,适配 Vite / Nuxt 3
    "declaration": true,       // 生成 .d.ts 文件
    "sourceMap": true,    // 生成 map
    "emitDeclarationOnly": false, // 只生成 .d.ts,不生成 JS(可选)
    "outDir": "dist",          // 输出目录
    "strict": true             // 开启严格模式,提升类型安全
  },
 // "files": ["src/index.ts","src/test22.ts","src/test22.d.ts"], // 直接指定具体文件
   "include": ["src/**/*"]   //匹配正则所有文件
}

配置介绍

{ 
  "compilerOptions": { 
     /* 基础 */
    "target": "ESNext",    // 目标 JavaScript 版本,ESNext 可支持最新特性
    "module": "ESNext",        // 使用 ES 模块,适配 Vite / Nuxt 3
    "declaration": true,       // 生成 .d.ts 文件
    "sourceMap": true,    // 生成 map
    "emitDeclarationOnly": false, // 只生成 .d.ts,不生成 JS(可选)
    "outDir": "dist",          // 输出目录
    "outFile" : 'abc.js',  // 指定一个文件输出
    "strict": true             // 开启严格模式,提升类型安全
    "rootDir": '' ,  // 指定编译的根目录
    "allowJs": true,    // 允许编译 javascript 文件
    "checkJs": true,   // 报告 javascript 文件中的错误
    "jsx": "preserve",    // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "removeComments": true,        // 删除编译后的所有的注释
    "noEmit": true,                // 不生成输出文件

     /* 模块解析 */
     "moduleResolution": "node",    // 模块解析策略
     "baseUrl": "./",               // 模块解析根路径
     "paths": {},                   // 路径别名
     "rootDirs": [],                // 虚拟根路径
     "typeRoots": [],               // 类型声明根路径
     "types": [],                   // 引入的类型声明
     "allowSyntheticDefaultImports": true, // 允许默认导入
     
     /* Source Map 配置 */
     "sourceRoot": "./",            // 源码根路径 (调试)
     "mapRoot": "./",               // map 文件根路径 (调试)
     "inlineSourceMap": true,       // 内联 source map
     "inlineSources": true,         // 内联源码
     
     /* 严格模式 */
     "strict": true,                // 启用所有严格检查
     "noImplicitAny": true,         // 隐式 any 报错
     "strictNullChecks": true,      // 严格 null 检查
     "noImplicitThis": true,        // 隐式 this 报错
     "alwaysStrict": true,          // 严格模式 (use strict)

     /* 额外检查 */
     "noUnusedLocals": true,        // 未使用局部变量报错
     "noUnusedParameters": true,    // 未使用参数报错
     "noImplicitReturns": true,     // 非所有路径都有返回值报错
     "noFallthroughCasesInSwitch": true, // switch 穿透报错


     /* 其他配置 */
     "experimentalDecorators": true,// 启用装饰器
     "emitDecoratorMetadata": true  // 装饰器元数据


  },
 //   "files": ["src/index.ts","src/test22.ts","src/test22.d.ts"], // 直接指定具体文件
      "include": ["src/**/*"]   //匹配正则所有文件 
   // "compileOnSave": true,  // 保存自动编译 
   // "exclude": [],   //排除文件
   // "extends": "",   // 继承配置  
}

vue 或 nuxt 常用的配置

{
  "compilerOptions": {
    "target": "ESNext",                     // 目标 JavaScript 版本,ESNext 可支持最新特性
    "module": "ESNext",                      // 使用 ES 模块,适配 Vite / Nuxt 3
    "moduleResolution": "Node",              // Node 方式解析模块,适用于 Vue3 和 Vite
    "strict": true,                           // 开启严格模式,提高类型安全
    "jsx": "preserve",                        // 允许使用 JSX,适用于 Vue 3 + TSX 组件
    "esModuleInterop": true,                  // 兼容 CommonJS 和 ES 模块
    "allowSyntheticDefaultImports": true,     // 允许默认导入 CommonJS 模块
    "resolveJsonModule": true,                // 允许导入 JSON 文件
    "isolatedModules": true,                  // 适配 Nuxt 3,避免 `import type` 相关错误
    "noImplicitAny": true,                    // 禁止隐式 `any` 类型,增强类型安全
    "skipLibCheck": true,                     // 跳过库的类型检查,加快编译速度
    "forceConsistentCasingInFileNames": true, // 强制文件名大小写一致,避免跨平台问题
    "baseUrl": ".",                           // 允许使用 `paths` 进行别名配置
    "paths": {
      "@/*": ["./*"]                          // 允许 `@` 作为 `src` 目录的别名
    }
  },
  "include": ["./**/*.ts", "./**/*.d.ts", "./**/*.tsx", "./**/*.vue"], // 指定要编译的文件
  "exclude": ["node_modules", "dist", ".output"]  // 排除不需要编译的目录
}

兼容es5的配置

{ 
  "compilerOptions": { 
    "target": "ES5",    //没有Promise
    "module": "ESNext",     
    "declaration": true,       // 生成 .d.ts 文件
    "sourceMap": true, 
    "outDir": "dist",          // 输出目录
    "strict": true             // 开启严格模式,提升类型安全
  }, 
  "include": ["src/**/*"]
}

index.ts

const a2:Promise<string> = Promise.resolve('xxx')

这时候代码Promise会报错,可以进入 lib,让检查通过,但是生成的代码在老的浏览器 ie9还是会报错。

"lib": ["ES2015", "DOM"],  //DOM用于console报错

最好的方案还是加入垫片 npm install es6-promise

vue中应用

#自动化构建,不用每次自己执行tsc 转化
npm i webpack webpack-cli webpack-dev-server ts-loader typescript html-webpack-plugin
//vue 安装 
vue add @vue/typescript

webpack.config.js

const htmlwebpackPlugin = require("html-webpack-plugin")
module.exports = {
    entry: './index.ts',
    output: {
        filename: "main.js",
    },
    resolve:{
        //配置省略文件路径的后缀名 不推荐,如果有同名就over了
        extensions:['.js','.ts','.tsx',]
    },
    module:{
        rules:[
            {
                test:/\.ts$/,
                use:[
                    {
                        loader: 'ts-loader'
                    }
                ],
                exclude: /node_modules/
            }
        ]
    },
    plugins: [
        new htmlwebpackPlugin({
            template:"./public/index.html", 
        })
    ]
}

touch tsconfig.json

{
  "compilerOptions": { 
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",     
    "strict": true,            
    "esModuleInterop": true,     
    "experimentalDecorators": true, 
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

packge.json

{
  "name": "testts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ts-loader": "^6.0.0",
    "typescript": "^3.7.5",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

.vue的ts处理

//types/index.ts
export type Feature = {
  id: number,
  name: string
}
//使用自定义类型,HelloWorld.vue
<template>
  <div>
  <!--修改模板-->
    <li v -for= "feature in features" : key = "feature.id" > {{ feature.name }}
    </li> 
  </div> 
</template> 
< script lang = 'ts' >
// 导入接口 
import { Feature } from "@/types";
@Component
export default class Hello extends Vue {
  // 修改数据结构 
  features: Feature[] = [{ id: 1, name: "类型注解" }];
}
</script>

使用装饰器

<template>
  <div>
    <ul>
      <li v-for="feature in features" :key="feature">{{feature}}</li>
    </ul>
  </div>
</template> 

<script lang='ts'> 
import { Component, Prop, Vue } from "vue-property-decorator";

@Component
export default class Hello extends Vue {
  features: string[] = ["类型注解", "编译型语言"];
}
</script>