更加完善的 TS 教程:https://ts.xcatliu.com/
基础认知
基本类型
number、NaN(Not a Number)
string
boolean
undefined
null
unknown
any
void
虽然 Any 和 unknown 都可以表示不确定的类型,但是 Any 是可以添加对象参数属性的,而 unknown 则不能。
ts 中尽量少用 Any。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| let p1: any = {} p1.name = '江湖浪子'
let p2: unknown = {}
let p3: any = "金风玉露一相逢" p3 = "便胜却人间无数" let p4: unknown = "千里姻缘一线牵" p4 = 2.36
console.log(p1); console.log(p2); console.log(p3); console.log(p4);
|
联合类型
命名变量时使用 | 作为分隔符,写为其它类型,对该变量赋值时可以使用这几种类型的任何一种。
1 2
| let p1: number|string = 123 let p2: number|string = "诗酒趁年华"
|
类型断言
将不确定的类型断言为确定的类型,类似 Java 中的强转。
有时,我们需要获取特定类型中的属性,返回值若是联合类型或者 Any,则该属性无法正常获取,编译会报错,这时我们可以采取以下方式:
1 2 3 4 5 6 7
| let img = document.getElementById("#xxx") as HTMLImageElement img.src = ""
let img = <HTMLImageElement>document.getElementById("#xxx") img.height
|
数组类型
数组一般都是同类型的。
杂乱数组/泛型数组
数组中有多种不同类型的元素。
1 2
| let arr1: any[] = ["仰天大笑出门去", 9981, false] let arr2: (string|number|boolean)[] = [true, "123", 456]
|
多维数组
1
| let arr: number[][] = [[1], [12]]
|
还有另一种写法,但是不够通用,这里略过。
类数组
元组
元组的优点是防止越界。
1 2 3 4 5 6
| let arr1: [number, string, boolean] = [1, "true", false] console.log(arr1[0]); console.log(arr1[1]); console.log(arr1[2]);
|
接口
接口定义类信息,类似 Java 中的 class,存在只读属性、可选属性,默认属性必须选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| interface UserInfo { readonly id: number name: string age: number address?: string like?: string[] }
let user: UserInfo = { name: "jhlz" age: 18 } user.name = "江湖浪子" user.address = "上下五千年"
console.log(user); console.log(user.name);
|
接口中定义方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface UserInfo { name: string age?: number getName: () => string setAge: (age: number) => void }
let user: UserInfo = { name: "江湖浪子", getName() { return this.name }, setAge(age) { this.age = age } }
user.setAge(18)
console.log(user); console.log(user.name);
|
继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| interface User { gender: number }
interface UserInfo extends User { name: string age?: number }
let user: UserInfo = { name: "江湖浪子", gender: 1 }
|
接口交叉类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface User { gender: number }
interface UserInfo { name: string age?: number gender: number }
let user: UserInfo & User = { name: "江湖浪子", gender: 1 }
console.log(user);
|
交叉类型仅合并属性,而不处理属性的值或类型。如果两个接口中的相同属性具有不同的类型,代码将会报错。如果需要处理相同属性类型不同的情况,可以使用联合类型或其他适当的方法来处理。
类型别名
这个简单常用,给某些类型、函数定义别名,也可以用来定义对象。
定义对象
1 2 3 4 5 6 7 8 9 10 11 12
| type infoType = { readonly id : number name: string age?: number address?: string }
let info: infoType = { id: 1, name: "江湖浪子" } console.log(info);
|
interface 可以继承,type 不可以,只可以使用交叉类型;
interface 遇到重复命名的会自动合并;而 type 不会(直接报错);(实际项目中基本不会这样使用)
定义函数
1 2 3 4 5 6 7 8
| type fn = (para1: number, para2: number) => number
let sum: fn = (num1: number, num2: number) => num1 + num2 let diff: fn = (num1: number, num2: number) => num1 - num2
console.log(sum(1, 2)); console.log(diff(10, 3));
|
枚举
默认为数字枚举,并且从 0 开始增长,如果中间中断,那往下会继续从中断的地方自动增长。
1 2 3 4 5 6 7 8 9
| enum Color { RED, BLUE, GREEN, YELLOW = 10000, BLACK } console.log(Color.RED); console.log(Color.BLACK);
|
字符串枚举
1 2 3 4 5 6 7 8
| enum Color { RED="red", BLUE="blue", GREEN="green", } console.log(Color.RED);
|
接口枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| interface ColorInterface { color: Color, blue: Color.BLUE } enum Color { RED, BLUE, GREEN, YELLOW = 10000, BLACK } let c: ColorInterface = { color: Color.RED, blue: Color.BLUE }
console.log(c);
|
const 枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface ColorInterface { color: Color, blue: Color.BLUE } const enum Color { RED, BLUE, GREEN, YELLOW = 10000, BLACK }
let c: ColorInterface = { color: Color.RED, blue: Color.BLUE }
console.log(c);
|
主要作用就是节省因为枚举带来的资源消耗。
例如上面的接口枚举 ts 代码编译后如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var Color; (function (Color) { Color[Color["RED"] = 0] = "RED"; Color[Color["BLUE"] = 1] = "BLUE"; Color[Color["GREEN"] = 2] = "GREEN"; Color[Color["YELLOW"] = 10000] = "YELLOW"; Color[Color["BLACK"] = 10001] = "BLACK"; })(Color || (Color = {})); var c = { color: Color.RED, blue: Color.BLUE }; console.log(c);
|
而使用 const 枚举编译后:
1 2 3 4 5
| var c = { color: 0 , blue: 1 }; console.log(c);
|
泛型
泛型的好处大家也都了解,每种语言基本都会提供泛型,使用方式也都差不多。先声明后使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function array<T>(a: T, b: T):T[] { return [a, b] }
function array<T extends string>(a: T, b: T): T[] { console.log(a.length); return [a, b] }
function array<T, E>(a: T, b: E):(T|E)[] { return [a, b] }
interface User { name: string age: number }
let user: User = { name: "江湖浪子", age: 18 }
function getValue<T extends keyof User>(u: User, key: T): User[T] { console.log(u[key]); return u[key] } getValue(user, "age")
|
内置对象
ECMAScript
DOM
常见的 html 标签都在 HTMLElementTagNameMap
接口中。
Location
Storage
localStorage
sessionStorage
document.cookie(string 类型)
tsconfig.json
生成该文件的命令:tsc -init
该文件的说明可以查阅 官网,也可以百度网上查询,都有很多详解。下面也是从网上找的部分常用的配置项说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| { "compilerOptions": { "target": "ES6", "module": "ES6", "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], "allowJs": true, "checkJs": true, "outDir": "./dist", "rootDir": "./", "removeComments":true,
"strict": true, "alwaysStrict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, "strictBindCallApply": true, "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true,
"baseUrl": "./", "paths": { "jquery": ["node_modules/jquery/dist/jquery.min.js"] }, } }
|
声明文件 d.ts
声明全局的变量和函数等
1 2 3
| declare var Hello:string
declare function getValue<T>(param : T):string
|
装饰器
装饰器是 ES7 提出的实验性功能,默认是关闭的,可以使用以下选项进行开启:
tsconfig.json1 2
| "experimentalDecorators": true, "emitDecoratorMetadata": true
|
类装饰器
在不改变原有代码的前提下,实现在原有逻辑前后增加功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const app: ClassDecorator = (fn) => { console.log(fn) fn.prototype.userName = "江湖浪子" } @app class User {
} let u = new User() console.log((u as any).userName)
const app = (str: string): ClassDecorator => { return (fn) => { fn.prototype.userName = str fn.prototype.getUserName = () => { console.log(fn.prototype.userName) } } }
@app("jhlz") class User {
}
let u = new User() console.log((u as any).userName)
|