一 概述
- TypeScript是JavaScript的超集,支持ES6+标准
- 由微软开发的自由和开源的变成语言
- 可编译为纯JS
二 语法
1 基础类型
TypeScript相比JavaScript而言最大的区别就是 类型
1.1 布尔值
1
| let isDone : boolean = false;
|
1.2 数字
和JavaScript一样,TypeScript中的所有数字都是浮点数,使用类型number
表示。
1 2 3 4
| let dec : number = 6; let hex : number = 0x0C; let bin : number = 0b01; let oct : number = 0o74;
|
1.3 字符串
使用string
表示文本数据类型,可以使用"
或者'
来表示字符串
1
| let name : string = "curry";
|
模板字符串
模板字符串使用(反引号)包围,并且可以在其中使用
${ expr }
嵌入表达式,expr
既可以是模板字符串也可以是可计算的表达式。
1 2 3
| let name : string = `zhhc`; let birth_year : number = 2002; let sentence : string = `hello, i am ${ name }, and my age is ${ 2022 - birth_year }`;
|
1.4 数组
两种定义方式
- ```typescript // 类型后面加[] let list : number[] = [1, 2, 3];
1 2 3 4
| 2. ```typescript let list : Array<number> = [1, 2, 3];
|
1.5 元组
元组类型表示 已知数量和类型 的数组,各元素的类型
不必相同
1
| let x : [string, number] = ['typescript', 10];
|
1.6 枚举
枚举是TypeScript中多出来的,和C语言等高级语言类似,可以为一组数组赋予有意义的名字,帮助理解。
1 2
| enum Color {Red, Green, Blue} let c : Color = Color.Red;
|
可以通过枚举值得到其名字
1 2
| enum Color {Red = 1, Green, Blue} let green_name : string = Color[2];
|
1.7 任意值
任意值(any)其实就像是JavaScript中的变量一样,可以被赋予任何类型的值,编译器不会进行检查。
对于Object类型的变量,你可以赋任何值,但是不能任意调用方法,即便它真的有这些方法。
1 2 3 4 5 6 7 8
| let Any : any = 4; Any = "Curry"; Any = true;
let anyObject : Object = 4; anyObject.function();
let list : any[] = [1, true, "free"];
|
1.8 空值
空值(void
)表示没有任何类型,一般用于函数没有返回值
1 2 3
| function func() : void{ }
|
声明变量没有什么意义,只能赋值null
或undefined
1
| let void_v : void = null;
|
1.9 Null / Undefined
默认情况下 null
和 undefined
是所有类型的子类型,即可以将它们赋值给其他类型的变量,但是如果指定了--strictNullChecks
,那么只能赋值给各自类型了
1 2 3 4 5 6 7
| let n : number = null; let s : string = undefined;
let nll : null = null; let udfd : undefined = undefined;
|
1.10 Never
never
表示永不存在值的类型。
对于函数,修饰那些 【抛出异常】 或 【根本就不会有返回值】的函数。
1 2 3 4 5 6 7 8 9
| function erro(message : string) : never { throw new Error(message); }
function deadLoop() : never { while(true){ } }
|
对于变量,never
类型是任何类型的子类型,所以可以赋值给任何类型;但是never
类型的变量只能由never
赋值。
1 2 3
| let a : number = never;
let a : never = any;
|
1.11 类型断言
告诉编译器某个变量的类型
1 2 3 4 5 6
| let s : any = "i am a string";
let len : number = (<string>s).length;
let len : number = (s as string).length;
|
2 变量声明
TypeScript
中支持let
和const
,JS在ES6就支持了,主要是let
和var
的区别:主要就是var
是函数级作用域,但是let
是块级作用域。
3 接口
TypeScript中最核心的原则之一就是
类型,而接口就是为类型检查 定义契约。
像下面这个例子,printConfig
函数接受一个符合config
接口的参数,而config
接口定义了
需要含有值类型为string
的name
属性
1 2 3 4 5 6 7 8 9 10
| interface config{ name : string; }
function printConfig(Aconfig : config){ console.log(config.name); }
let cfg = {name : "my config", size : 10}; printConfig(cfg);
|
可选属性
这里重点需要注意的是:一旦接口中定义了可选的属性,那么被修饰的变量中只能存在要求的属性和可选的属性,不能出现其中不存在的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface config{ name?: string; size?: number; }
function printConfig(Aconfig : config){ console.log(config.name); }
printConfig({name : "my config", siz : 10});
printConfig({name : "my config", siz : 10} as config)
let c = {name : "my config", siz : 10} printConfig(c);
|
函数类型
下面这个接口就定义了一个函数类型,参数是两个number
类型的值,返回类型是boolean
1 2 3 4 5 6 7 8
| interface func_temp{ (n1: number, n2: number) : boolean; }
let func1: func_temp; func1 = function(n1, n2){ return n1 > n2; }
|
可索引的类型
1、数字索引=》数组
1 2 3 4 5
| interface number_index{ [index : number] : string; }
let a : number_index = ["a", "b"];
|
2、字符串索引=》字典
1 2 3 4 5
| interface string_index{ [index : string] : string; }
let b : string_index = {x : "x", y : "y"};
|
3、
由于当使用number
来索引时,JavaScript会将它转换成string
然后再去索引对象。所以当两者一起使用时,number
索引返回值需要是string
索引返回值的子类型。
1 2 3 4 5 6 7 8 9 10 11 12
| class A{ }
class B extends A{ }
interface both_index{ [index : number] : B; [index : string] : A; }
|
类类型
这一部分就和其他面向对象高级语言中的类似了。
要注意类静态部分和实例部分的区别
1 2 3 4 5 6 7 8 9
| interface ACon{ new (); }
class A implements ACon{ constructor(){} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface ACon{ new () : AInter; }
interface AInter{ a_func(); }
function createA(acon : ACon) : AInter{ return new acon(); }
class A1 implements AInter{ constructor(){} }
class A2 implements AInter{ constructor(){} }
let a1 = createA(A1); let a2 = createA(A2);
|
混合类型
一个对象即可以同时作为函数和对象使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| interface Counter { (start: number): string; interval: number; reset(): void; }
function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; }
let c = getCounter(); c(10); c.reset(); c.interval = 5.0;
|
接口继承类
当使用接口A
去继承类B
时,它继承类B
的所有成员但不包括实现。所以某个类想要实现这个接口A
,这个类必须是类B
本身或者是类B
的子类,否则它不能拥有接口A
中的private
和protect
成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Control { private state: any; }
interface SelectableControl extends Control { select(): void; }
class Button extends Control implements SelectableControl { select() { } }
class Image implements SelectableControl { select() { } }
|
4 类
1、类型兼容:TypeScript使用的是结构性类型系统,当比较两种不同的类型时,并不在乎它们从何处而来,如果所有的成员的类型都是兼容的,我们认为它们的类型是兼容的。但是对于带有private
或者protected
成员的类型时,就需要private
和protect
成员来自于同一处声明时,才认为这两个类型是兼容的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class A { private name : string; constructor(){} }
class B extends A { constructor(){super();} }
class C{ private name : string; constuctor(){} }
let a = new A(); let b = new B(); let c = new C();
a = b; a = c;
|
2、参数属性:在构造函数中使用访问限定符修饰参数,使类的属性的声明和赋值合并到一处
1 2 3
| class Per{ constructor(private name : string){} }
|
3、存取器
TypeScript支持通过getter和setter来截获对对象成员的访问。
1 2 3 4 5 6 7 8 9 10 11
| class A { private _name : string; get name() : string{ return this._name; } set name(newName : string){ } }
|
getter和setter是会在访问相关属性时自动调用的,而不需要显式调用。
1 2 3
| let a = new A(); a._name = "zhhc"; console(a._name);
|
4、其他
抽象类、继承、访问修饰符
5、高级技巧
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Greet { static msg = "Hello!"; greeting : string; greet(){ if(this.greeting){ console.log(this.greeting); } else { console.log(Greet.msg); } } }
let GreetMaker : typeof Greet = Greet;
|
5 函数
1 2 3 4 5 6 7 8 9
| function add(x, y){ return x + y; }
let add = function(x, y) { return x + y; }
|
类型
1 2 3 4 5 6 7 8 9 10 11
| function add(x: number, y: number) : number{ return x + y; }
let add = function(x: number, y: number) : number{ retunrn x + y; }
let add: (x: number, y: number) => number = function(x: number, y: number) : number { return x + y; }
|
可选参数
注意:可变参数放最后
1 2 3 4 5 6 7
| function add(x: number, y: number, z?: number) : number{ if(z){ return x + y + z; } else { return x + y; } }
|
默认参数
1、默认参数可以放在必须参数的最后面,该参数可以省略
2、默认参数也可以放在必须参数的最前面,该参数不能省略,需要传递undefined
获得这个默认值
1 2 3 4 5 6 7 8 9 10 11 12
| function add(x: number, y = 10) : number{ return x + y; }
let a = add(1);
function add(x = 10, y: number) : number{ return x + y; }
let b = add(2); let c = add(undefined, 2);
|
剩余参数
剩余参数肯定是需要放在参数列表的最后
1 2 3 4 5 6 7
| function add(x: number, y: number, ...nums: number[]) : number{ int sum = x + y; for(int i = 0; i < nums.length; i++){ sum += nums[i]; } return sum; }
|
this
this是JS中的一个难点。在JavaScript中,this
的值在调用时才会确定,这导致了this值会随着调用上下文的改变而改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let per = { name : "zhhc", age : 21, hello : function(){ return function(){ msg = "I am " + this.name + ", " + this.age + "years old!"; return msg; } } }
let Hello = per.hello(); let message = Hello();
console.log(message);
|
解决方法是使用ES6的一个新特性,箭头函数。在箭头函数中,this
的值在创建时就确定下来了,箭头函数不会创建自己的this
,它只会从自己的作用域链的上一层继承this
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let per = { name : "zhhc", age : 21, hello : function(){ return () => { msg = "I am " + this.name + ", " + this.age + "years old!"; return msg; } } }
let Hello = per.hello(); let message = Hello();
console.log(message);
|
this参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| interface Person{ name: string; age: number; hello(this: Person): () => string; }
let per : Person = { name : "zhhc", age : 21, hello : function(this: Person){ return () => { msg = "I am " + this.name + ", " + this.age + "years old!"; return msg; } } }
let Hello = per.hello(); let message = Hello();
console.log(message);
|
明确地告诉typescript
,this
的类型是Person
,而不是any
其他
函数重载
6 泛型
泛型总的来说和Java的比较类似。
1、泛型函数
1 2 3
| function identity<T>(arg : T) : T { return arg }
|
2、泛型接口
1 2 3 4 5
| interface GenericFunc<T>{ (arg: T) : T; }
let fn : GenericFunc<number> = function(arg : number){ return 2 * arg; }
|
3、泛型类
1 2 3
| class Calc<T>{ add: (x: T, y: T) => T; }
|
4、泛型约束
由于泛型参数是针对所有类型,就会导致不能使用一些只有某些类型才具备的属性,例如length
,解决方法就是加上泛型约束,对泛型参数加上一定的限制。
1 2 3 4
| function getLen<T extends {length: number}>(arg: T) : T{ console.log(arg.length); return arg; }
|