环境搭建
#全局库 为了全局可以执行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 {
id: string;
name: string;
}
// error:Property 'id' is missing in type '{ name: string; }' but required in type 'UserInfo'
const xiaoming: UserInfo = {
name: 'xiaoming'
}
type NewUserInfo = Partial<UserInfo>;
const xiaoming: NewUserInfo = {
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 (...args: any[]) => any> = T extends (
...args: any[]
) => infer R
? R
: any;
type Func = (value: number) => string;
const foo: ReturnType<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>val 或 val 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'
*/
装饰器求值
类的定义中不同声明上的装饰器将按以下规定的顺序引用:
- 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个实例成员;
- 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个静态成员;
- 参数装饰器应用到构造函数;
- 类装饰器应用到类
类装饰器
类装饰器表达式会在运行时当做函数被调用,它由唯一一个参数,就是装饰的这个类。
//类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
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>