前言 TypeScript 基础
安装 TypeScript 1 2 3 4 npm install -g typescript tsc -V
TypeScript中的基本类型 类型声明 1 2 3 4 5 6 let 变量: 类型;let 变量: 类型 = 值;function fn (参数: 类型, 参数: 类型 ): 类型{ ... }
自动类型判断
TS拥有自动的类型判断机制
当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
类型
类型
例子
描述
number
1、-1、1.1
任意数字
string
‘hi’、”hi”、hi
任意字符串
boolean
true、false
布尔值true或false
字面量
其本身
限制变量的值就是该字面量的值
any
*
任意类型
unknown
*
类型安全的any
void
空值(undefined)
没有值(或undefined)
never
没有值
不能是任何值
object
{ name: ‘孙悟空’ }
任意的JS对象
array
[1,2,3]
任意JS数组
tuple
[4,5]
元素,TS新增类型,固定长度数组
enum
enum{A,B}
枚举,TS中新增类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 let num : number = 1 ;let str : string = "hello world" ;let bool : boolean = false ;let sex : "男" | "女" ;sex = "男" ; let val : any ;let e : unknown ;e = 1 ; e = true ; e = "字符串" ; let z : any ;let x : unknown ;let c : string ;z = 1 ; x = 1 ; c = z; if (typeof x === "string" ) c = x;c = x as string ; c = <string >x; let unusable : void = undefined ;function fn ( ): void {}function error (message : string ): never { throw new Error (message); } let obj : object ;obj = {}; obj = []; obj = () => {}; let o : { name : string };o = { name : "xxx" }; let ob : { name : string ; [propName : string ]: string };ob = {name : 'xxx' , x : 'xxx' } let fun : (a : number , b : number ) => number ;function info (x : string , ...args : string [] ) { console .log (x, args) } function fn (a : string , b ?: string ): string { return a + b; } function add (x : string , y : string ): string function add (x : number , y : number ): number function add (x : string | number , y : string | number ): string | number | undefined { if (typeof x === 'string' && typeof y === 'string' ) { return x + y } else if (typeof x === 'number' && typeof y === 'number' ) { return x + y } } console .log (add (1 , 2 ))console .log (add ('a' , 'b' ))let list : number [] = [1 , 2 , 3 ];let arr : Array <number > = [1 , 2 , 3 ];let arr1 : [string , number ];arr1 = ["xxx" , 2 ]; enum Gender { Male = "男" , Female = "女" , } let i : { name : string ; gender : Gender };i = { name : "xxx" , gender : Gender .Male }; let n : { name : string } & { age : number };n = { name : "xxx" , age : 10 }; type myType = 1 | 2 | 3 | 4 | 5 ;let j : myType;let k : myType;
编译选项 编译命令
tsconfing.json 文件 include
定义希望被编译文件所在的目录
默认值:[“**/*”]
示例:
1 2 "include" : [ "src/**/*" , "tests/**/*" ]
上述示例中,所有src目录和tests目录下的文件都会被编译
exclude
定义需要排除在外的目录
默认值:[“node_modules”, “bower_components”, “jspm_packages”]
示例:
1 "exclude" : [ "./src/hello/**/*" ]
上述示例中,src下hello目录下的文件都不会被编译
extends
示例:
1 "extends" : "./configs/base"
上述示例中,当前配置文件中会自动包含config目录下base.json中的所有配置信息
files
指定被编译文件的列表,只有需要编译的文件少时才会用到
示例:
1 2 3 4 5 6 7 8 9 10 11 12 "files" : [ "core.ts" , "sys.ts" , "types.ts" , "scanner.ts" , "parser.ts" , "utilities.ts" , "binder.ts" , "checker.ts" , "tsc.ts" ]
compilerOptions
编译选项是配置文件中非常重要也比较复杂的配置选项
在compilerOptions中包含多个子选项,用来完成对编译的配置
target
设置ts代码编译的目标版本
可选值:ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext
1 2 3 "compilerOptions" : { "target" : "ES6" }
lib
指定代码运行时所包含的库(宿主环境)
可选值:ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost ……
1 2 3 4 "compilerOptions" : { "lib" : [ "ES6" , "DOM" ] , }
module
设置编译后代码使用的模块化系统
可选值:CommonJS、UMD、AMD、System、ES2020、ESNext、None
1 2 3 "compilerOptions" : { "module" : "CommonJS" }
outDir
编译后文件的所在目录
默认情况下,编译后的js文件会和ts文件位于相同的目录,设置outDir后可以改变编译后文件的位置
1 2 3 "compilerOptions" : { "outDir" : "dist" }
outFile
将所有的文件编译为一个js文件
默认会将所有的编写在全局作用域中的代码合并为一个js文件,如果module制定了None、System或AMD则会将模块一起合并到文件之中
1 2 3 "compilerOptions" : { "outFile" : "dist/app.js" }
rootDir
指定代码的根目录,默认情况下编译后文件的目录结构会以最长的公共目录为根目录,通过rootDir可以手动指定根目录
1 2 3 "compilerOptions" : { "rootDir" : "./src" }
allowJs
checkJs
1 2 3 4 "compilerOptions" : { "allowJs" : true , "checkJs" : true }
removeComments
noEmit
sourceMap
严格检查
strict:启用所有的严格检查,默认值为true,设置后相当于开启了所有的严格检查
alwaysStrict:总是以严格模式对代码进行编译
noImplicitAny:禁止隐式的any类型
noImplicitThis:禁止类型不明确的this
strictBindCallApply:严格检查bind、call和apply的参数列表
strictFunctionTypes:严格检查函数的类型
strictNullChecks:严格的空值检查
strictPropertyInitialization:严格检查属性是否初始化
额外检查
noFallthroughCasesInSwitch:检查switch语句包含正确的break
noImplicitReturns:检查函数没有隐式的返回值
noUnusedLocals:检查未使用的局部变量
noUnusedParameters:检查未使用的参数
高级
allowUnreachableCode:检查不可达代码,可选值:true 忽略不可达代码、false 不可达代码将引起错误
noEmitOnError:有错误的情况下不进行编译,默认值:false
面向对象 定义类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class 类名 { 属性名: 类型; constructor (参数: 类型 ) { this .属性名 = 参数; } 方法名() { .... } static 属性名: 类型 = 属性值 static 方法名() { ... } } class Person { name : string ; age : number = 18 constructor (name : string ) { this .name = name; } sayHello ( ) { console .log (`大家好,我是${this .name} ` ); } static PI = 3.1415926 ; static sum (num1 : number , num2 : number ){ return num1 + num2 } } const p = new Person ('孙悟空' , 18 );p.sayHello (); console .log (Person .PI , Person .sum (123 , 456 ));
类的继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class A { num : number ; constructor (num : number ) { this .num = num; } fn ( ) { console .log (`父类中的fn方法!` , this .num ); } } class X extends A { protected name : string ; constructor (num : number , name : string ) { super (num); this .name = name; } fn ( ) { super .fn () console .log (`字类中的fn方法!` ); } }
抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 abstract class Animal { abstract run (): void ; bark ( ) { console .log ('动物在叫~' ); } } class Dog extends Animals { run ( ) { console .log ('狗在跑~' ); } }
封装 对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装
默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在 TS 中可以对属性的权限进行设置
静态属性(static)
声明为 static 的属性或方法不再属于实例,而是属于类的属性;
只读属性(readonly):
如果在声明属性时添加一个 readonly,则属性便成了只读属性无法修改
TS中属性具有三种修饰符:
public(默认值)可以在类、子类和对象中修改
protected 可以在类、子类中修改
private 可以在类中修改
示例:
public:
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 class C { constructor (public name : string , public age : number ) { } } class Person { public name : string ; public age : number ; constructor (name : string , age : number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } } class Employee extends Person { constructor (name : string , age : number ){ super (name, age); this .name = name; } } const p = new Person ('孙悟空' , 18 );p.name = '猪八戒' ;
protected:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { protected name : string ; protected age : number ; constructor (name : string , age : number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } } class Employee extends Person { constructor (name : string , age : number ){ super (name, age); this .name = name; } } const p = new Person ('孙悟空' , 18 );p.name = '猪八戒' ;
private:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { private name : string ; private age : number ; constructor (name : string , age : number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } } class Employee extends Person { constructor (name : string , age : number ){ super (name, age); this .name = name; } } const p = new Person ('孙悟空' , 18 );p.name = '猪八戒' ;
属性存取器
对于一些不希望被任意修改的属性,可以将其设置为 private
直接将其设置为 private 将导致无法再通过对象修改其中的属性
我们可以在类中定义一组读取、设置属性的方法,这种对属性读取或设置的属性被称为属性的存取器
读取属性的方法叫做 setter 方法,设置属性的方法叫做 getter 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person { private _name : string ; constructor (name : string ){ this ._name = name; } get name (){ return this ._name ; } set name (name : string ){ this ._name = name; } } const p1 = new Person ('孙悟空' );console .log (p1.name );p1.name = '猪八戒' ;
接口 接口的作用类似于抽象类,不同点在于:接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法;
接口主要负责定义一个类的结构,接口可以去限制一个对象的接口:对象只有包含接口中定义的所有属性和方法时才能匹配接口;
同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性;
注:接口可以重复申明,之后的会合并之前申明的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface Person { name : string ; sayHello ():void ; } class Student implements Person { constructor (public name : string ) { this .name = name } sayHello ( ) { console .log ('大家好,我是' +this .name ); } }
接口补充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 interface IPerson { readonly id : number name : string sex ?: string } interface SearchFunc { (source : string , subString : string ): boolean } const mySearch : SearchFunc = function (source : string , sub : string ): boolean { return source.search (sub) > -1 } interface A { fn1 (): void } interface B { fn2 (): void } class C implements A, B { fn1 ( ) { console .log ('fn1' ) } fn2 ( ) { console .log ('fn2' ) } } interface LightableAlarm extends Alarm , Light {}
泛型 泛型(Generic):定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定);此时泛型便能够发挥作用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 function test (arg : any ): any { return arg; } function test<T>(arg : T): T{ return arg; } test (10 ) test<number >(10 ) function test<T, K>(a : T, b : K): K{ return b; } test<number , string >(10 , "hello" ); class MyClass <T>{ prop : T; constructor (prop : T ){ this .prop = prop; } } interface MyInter { length : number ; } function test<T extends MyInter >(arg : T): number { return arg.length ; } interface IbaseCRUD <T> { data : T[] add : (t : T ) => void getById : (id : number ) => T } class User { id ?: number name : string age : number constructor (name, age ) { this .name = name this .age = age } } class UserCRUD implements IbaseCRUD <User > { data : User [] = [] add (user : User ): void { user = { ...user, id : Date .now () } this .data .push (user) console .log ('保存user' , user.id ) } getById (id : number ): User { return this .data .find (item => item.id === id) } } const userCRUD = new UserCRUD ()userCRUD.add (new User ('tom' , 12 )) userCRUD.add (new User ('tom2' , 13 )) console .log (userCRUD.data )
声明文件 1 2 3 4 5 6 7 8 9 10 11 declare var jQuery : (selector : string ) => any jQuery ('#foo' )
内置对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let b : Boolean = new Boolean (1 )let n : Number = new Number (true )let s : String = new String ('abc' )let d : Date = new Date ()let r : RegExp = /^1/ let e : Error = new Error ('error message' )b = true const div : HTMLElement = document .getElementById ('test' )const divs : NodeList = document .querySelectorAll ('div' )document .addEventListener ('click' , (event : MouseEvent ) => { console .dir (event.target ) }) const fragment : DocumentFragment = document .createDocumentFragment ()
Vue3 对 TS 支持 参考:TypeScript 与组合式 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 interface Props { name ?: string , title : number , status ?: boolean } const props = defineProps<Props >()import { Props } from './Props' defineProps<Props >() const props = withDefaults (defineProps<{ name : string title ?: string num : number [] }>(), { title : '测试标题' , num : () => [10 , 20 , 30 , 40 , 50 ] }) const emit = defineEmits<{ (e : 'change' , id : number ): void (e : 'update' , value : string ): void }>() const year = ref<string | number >('2020' )interface Book { title : string year ?: number } const book : Book = reactive ({ title : 'Vue 3 指引' })const double = computed<number >(() => { }) function handleChange (event : Event ) { console .log ((event.target as HTMLInputElement ).value ) }
TypeScript 补充
1 2 3 4 5 let obj : unknown = { a : 1 , b : (): number => 2 };obj.b ; obj.ccc ();
1 2 3 4 5 6 7 8 9 let a : object = {b : 1 }console .log (a.b )
1 2 3 4 5 6 7 let arr : number [][] = [[1 ], [2 ]]let arr : []<Array ><number > = [[1 ], [2 ]]function ( ) { let a :IArguments = arguments }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface Obj { name : string ; fn : (this : Obj ) => string ; } let obj : Obj = { name : "foo" , fn (this : Obj ): string { return this .name ; }, }; console .log (obj.fn ())
1 2 3 4 5 6 7 8 let el1 : HTMLDivElement | null = document .querySelector ("div" );let el2 : NodeList = document .querySelectorAll ("div" );let el3 : NodeListOf <HTMLDivElement > = document .querySelectorAll ("div" );let el4 : NodeListOf <HTMLDivElement | Element > = document .querySelectorAll ("div" ); let loc : Storage = localStorage ;let lo : Location = location;let co :string = document .cookie let P : Promise <number > = new Promise ((v ) => v (1 ));
1 2 3 4 5 6 7 8 let arr : [number , number , string ] = [1 , 2 , '3' ];arr.push ('5' ) console .log (arr)let arr1 : readonly [number , string ] = [1 , '2' ];let arr1 : readonly [x : number , y ?: string ] = [1 , "2" ];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const enum Types { No = "No" , Yes = 1 , } enum Enum { fall } let a = Enum .fall ;console .log (a); let nameOfA = Enum [a]; console .log (nameOfA);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 namespace a { export const a1 = 1 ; export namespace b { export const a2 = 2 ; } } console .log (a.a1 );console .log (a.b .a2 );import B = a.b ;console .log (B.a2 );
三斜线指令:在编译过程中要引入的额外的文件、三斜线指令仅可最顶端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 namespace A { export const a = 'a' } namespace A { export const b = 'b' } console .log (A);
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 declare var 声明全局变量declare function 声明全局方法declare class 声明全局类declare enum 声明全局枚举类型declare namespace 声明(含有子属性的)全局对象interface 和 type 声明全局类型/// <reference /> 三斜线指令 // 声明文件手写参考 express.d.ts ( 最好统一放在 typings文件夹下 ) declare module 'express' { interface Router { get (path : string , cb : (req : any , res : any ) => void ): void } interface App { use (path : string , router : any ): void listen (port : number , cb ?: () => void ): void } interface Express { (): App Router (): Router } const express : Express export default express }