Typescript
基础类型
// 布尔 boolean
let flag:boolean = true;
// 数字类型 number
let num:number = 100;
// 字符串类型 string
let str:string = "Hello";
// 数组类型 array
let arr:number[] = [1,2,3,4,5];
let arr:Array<number> = [1,2,3,4,5];
// 元组类型 tuple(数组的增强可指定多种类型)
let arr:[string,number] = [1,"a"];
// 枚举类型 enum(用于标识状态和固定值)
enum Flag{
success=1,
error=-1
};
let f:Flag = Flag.success;
enum Color {red=2,blue,orange}; // 如果不定义值,则是其索引值
let c:Color = Color.blue; // 3,上一个值有定义则在此基础上+1
// 任意类型 any(跟js原本的变量无区别)
let str:any = "Hello";
let num:any = 123;
// null 和 undefined
let num:number | undefined;
num = 123;
// void 类型(无任何值类型,常用于方法无返回值)
function fun():void{
}
// never 从来不会出现的类型(通常不建议使用)
let num:never;
num = (()=>{
throw new Error("错误");
})();
联合类型
即允许多个类型同时存在
let val:number|string;
val = 123;
val = "Hello";
交叉类型
使用 &
符号将多个类型叠加到一起成为的一种新的类型,基包含了多个类型的所有特性。
interface Student {
id: string;
age: number;
}
interface Worker {
companyId: string;
}
type A = Student & Worker; // A 此时包含了 Student 和 Worker 特性
let x: A;
x.age = 5; // 合法
x.companyId = 'CID5241'; // 合法
x.id = 'ID3241'; // 合法
// 合并对象演示
function extend<T, U>(first: T, second: U): T & U {
let result = {} as T & U // result 是要返回结果,类型断言为 T&U
for(let id in first){ // 不能将类型 T 分配给类型 T&U,故需使用断言
result[id] = first[id] as any
}
for(let id in second){
result[id] = second[id] as any
}
return result; // 返回结果,类型是 T & U
}
class A{
constructor(public name: string){}
}
class B{
constructor(public id:number,public age:number){}
}
let c = extend(new A('Hello'), new B(100,12));
c.name; // 'Hello'
c.id; // 100
c.age; // 12
断言
断言不是类型转换,通过断言告诉编译器『我完全知道自己在干什么』
// 尖括号语法
let val: any = "this is a string";
let len: number = (<string>val).length;
// as语法
let val: any = "this is a string";
let len: number = (val as string).length;
显示赋值断言
显式赋值断言是 TS2.7 引入的一个新语法。明确告诉编译器:该属性或变量已经赋过值了。
// 用在类属性上
class C {
foo!: number;
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
// 用在变量上
let x!: number[];
initialize();
x.push(4);
function initialize() {
x = [0, 1, 2, 3];
}
函数
// 参数和返回值类型都要指定
function run(a:number, b:number):string{
return a+b.toString();
}
// 可选参数
function run(a:number, b?:number):string{
let val = b ? b : a;
return val.toString();
}
// 默认参数
function run(a:number=100):string{
return a.toString();
}
// 剩余参数(扩展运算符来接收参数)
function run(...arg:number[]):number{
let result = 0;
for(let i:number=0; i<arg.length; i++){
result += arg[i];
};
return result;
}
// 函数重载(即多个同名函数,但参数不一样。即会出现函数重载)
function run(a:string):string;
function run(a:number):string;
function run(a:string|number){
return typeof a === 'string' ? "字符" : "数字";
};
run("Hello"); // 字符
run(100); // 数字
类
/*
类的三个修饰符
---
public 公有 在类、子类、类外都可访问(默认)
protected 保护 在类、子类可访问
priveate 私有 在类可访问,子类、类外都不可访问
*/
// 类的基本结构
class Person{
name:string; // 属性,前面省略了 public 关键词
constructor(a:string){ // 构造函数
}
run():void{ // 类的方法,前面省略了 public 关键词
console.log(this.name);
}
static run():void{ // 静态方法,通过 Person.run 调用
}
}
// 类的继承
class Person1 extends Person {
constructor(a:string){
super(a); // 调用父类的构造函数
}
work():void{ // 子类自己的方法
}
}
// 多态,即父类定义一个方法不去实现,让继承它的子类去实现,每个子类有不同的表现
class Animal{
name:string;
constructor(a:string){
}
eat():void{ // 定义一个方法让子类去实现
}
}
class Dog extends Animal{
constructor(a:string){
super(a); // 调用父类的构造函数
}
eat():void{ // 子类自己实现的方法
console.log("吃骨头");
}
}
// 抽象类,即只供其它类继承使用基类,本身不能被实例化。使用 abstract 关键字定义抽象类和抽象方法
abstract class Animal{
name:string;
constructor(a:string){
this.name = a;
}
abstract eat():void; // 定义一个方法让子类去实现,abstract 方法只可在 abstract 类中
}
class Dog extends Animal{
constructor(a:string){
super(a); // 调用父类的构造函数
}
eat():void{ // 子类中必须实现父类中定义的抽象方法
console.log("吃骨头");
}
}
存取器
class A {
constructor(){}
private _name:string | undefined;
set name(str:string) {
this._name = str;
}
get name():string{
return `My name is ${this._name}`;
}
}
let a = new A();
a.name = "Andy";
a.name; // "My name is Andy"
三斜线指令
仅能放在文件的最顶端才可生效。
/// <reference path="..." />
// 常用于引入 `.d.ts`文件。用于告诉编译器在编译时要引入的额外文件
/// <reference types="node" />
// 引入到声明文件,表明这个文件使用了 @types/node/index.d.ts 里面声明的名字
/// <amd-module name="moduleName" />
// 用于指定 AMD 模块的名称(否则默认为匿名)
/// <reference no-default-lib="true"/>
// 标记为默认库,lib.d.ts 文件就有此项
/// <reference lib="es2017.string" />
// 等效于使用 -lib es2017.string 进行编译
接口
规范的定义,用于起到限制的作用。接口不关心内部状态数据,也不关心实现细节。只是限制必须提供某些方法。类于 java,同时增加了接口类型、函数、可索引、类等。
// 属性接口
interface FullName {
firstName:string;
secondName?:string; // 可选属性,该参数可传可不传
}
function fun(name:FullName):void{
console.log(name.firstName);
}
let obj = { // 传入的参数必须包含 firstName、secondName是可选的
firstName:"张",
secondName:"三"
};
fun(obj);
// 函数类型接口(对方法传入的参数以及返回值进行约束)
interface encrypt {
(key:string, value:string):string;
}
let md5:encrypt = function(key:string, value:string):string{
return key+value;
}
md5("a","b");
// 可索引接口(约束数组、对象)
interface UserArr{ // 约束数组
[index:number]:string;
}
let arr:UserArr=['a','b'];
interface UserObj{ // 约束对象
[index:string]:string
}
let obj:UserObj={name:'a'}
// 类的类型接口(对类的约束,和抽象类相似)
interface Animal{ // 约束类
name:string;
eat(str:string):void;
}
class Dog implements Animal { // 实现接口
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string):void{
}
}
// 接口扩展(接口继承接口)
interface Animal{
eat():void;
}
interface Person extends Animal { // 继承接口
work():void;
}
class Web implements Person{ // 实现接口
name:string;
constructor(name:string){
this.name = name;
}
eat():void{
}
work():void{
}
}
泛型
解决类、接口方法的复制用,并对不特定数据类型支持。
// T即是泛型,具体什么类型是调用这个方法的时候决定的
function fun<T>(value:T):T{ // 定义了传入与返回的类型一至
return value;
}
fun<number>(123); // 传入的是数字,返回也必须是数字
// 泛型类
class Min<T>{
list:T[]=[];
add(value:T):void{
this.list.push(value);
}
min():T{
let minVal = this.list[0];
for(let i:number=0; i<this.list.length; i++){
if(minVal > this.list[i]){
minVal=this.list[i];
};
};
return minVal;
}
}
let m1 = new Min<number>(); // 实例化时才决定了 T 的类型为数字
m1.add(1);
m1.add(2);
m1.min(); // 1
let m2 = new Min<string>(); // 实例化时才决定了 T 的类型为字符
m2.add("a");
m2.add("b");
m2.min(); // "a"
// 泛型接口(方式一)
interface Config{ // 定义的泛型接口
<T>(value:T):T;
}
let fun:Config = function<T>(value:T):T { // 方法实现跟接口一致
return value;
}
fun<string>("a"); // "a",使用时才决定了其类型
fun<number>(1); // 1,使用时才决定了其类型
// 泛型接口(方式二)
interface Config<T>{ // 定义泛型接口
(value:T):T;
}
function fun<T>(value:T):T{ // 方法实现跟接口一致
return value;
}
let f:Config<string>=fun; // 决定了其类型
fun("a"); // "a"
混入 Mixins
类继承之外的一种通过可重用组件创建类的方式。通过把类当成接口,又不实现其定义的方法并具有混入类的属性及方法。
class Mixin1{ // 混入类1
constructor(public name:string){}
log1():void{
console.log("方法一",this.name);
}
}
class Mixin2{ // 混入类2
constructor(public age:number){}
log2():void{
console.log("方法二",this.age);
}
}
class A implements Mixin1,Mixin2 { // 把 Mixin1、Mixin2 当成接口
name!:string; // 定义占位,因为要实现接口中的属性
age!:number; // 定义占位,因为要实现接口中的属性
log1!:()=>void; // 定义占位,因为要实现接口中的方法
log2!:()=>void; // 定义占位,因为要实现接口中的方法
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
log():void{
console.log("自身的方法");
}
}
// 混入方法,遍历 Mixins 的所有方法、属性,并复制到定义的占位目标上去
function applyMixins(base:any, mixins:any[]){
mixins.forEach(mixin => {
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
base.prototype[name] = mixin.prototype[name];
});
});
}
// 应用混入
applyMixins(A,[Mixin1,Mixin2]);
let a = new A("Andy",12);
a.log();
a.log1();
a.log2();
文件模块
// export 可多次调用
// module.ts
export function module1():any[]{
return ["a"]
};
export function module2():string{
return "a";
};
// 或
function module1():any[]{
return ["a"]
};
function module2():string{
return "a";
};
export {module1, module2}; // 统一暴露
// main.ts
import {module1, module2 as m2} form "./module";
module1(); // ["a"]
m2(); // "a"
// export.default 每个文件内只能用一次
// module.ts
export.default function():void{
}
// main.ts
import module form "./module";
module();
命名空间
与 Java、c# 基本一致
// 命名空间下的接口、类默认均为私有
namespace A{
interface Animal {
name:string;
eat():void;
}
// 使用 export 暴露出去才可在外部调用
export class Dog implements Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
}
}
}
let d = new A.Dog("2ha");
d.eat();
// 命名空间也可以使用 export 暴露出去被外部调用
export namespace A{
interface Animal {
name:string;
eat():void;
}
export class Dog implements Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
}
}
}
装饰器
ES7的标准,装饰器本身就是一个方法。装饰器只能用于类、类的方法之上(可多个)。普通函数不能使用,因为普通函数存在变量提升。
// 类的装饰器方法
function logClass(str:string){
return function(target:Function){
target.prototype.url = str;
}
}
@logClass("http://a")
class HttpClient{
url?:string;
constructor(){
}
getData():void{
}
}
let http:HttpClient = new HttpClient();
http.url; // "http://a"
// 属性装饰器
function logProperty(str:string){
return function(target:any, name:string){
target[name] = str; // target 为类的原型对象
}
}
class HttpClient{
@logProperty("a")
url:string|undefined;
constructor(){
}
getData():void{
}
}
let http:HttpClient = new HttpClient();
console.log(http.url) // "a"
// 方法装饰器(用得较多)
function logFun(str:string){
return function(target:any,name:string,des:PropertyDescriptor):PropertyDescriptor{
let oldValue:Function = target[name];
// 修改方法。注意:ES5 、ES6的描述内容有差别
des.value = function(){
console.log("调用了装饰器");
return oldValue.call(this,str);
}
return des;
}
}
class HttpClient{
constructor(){
}
@logFun("a")
getData(str:string):string{
return str;
}
}
let http:HttpClient = new HttpClient();
console.log(http.getData("b")) // "a"
// 方法参数装饰器(常用来扩展类,使用场景较少)
function logParams(str:string){
return function(target:any,name:string,index:Number):void{
console.log(target); // target 类的原型链
console.log(name); // name 方法名称
console.log(index); // index 参数的索引
}
}
class HttpClient{
constructor(){
}
getData(@logParams("uuid") uid:string){
}
}
执行顺序:属性装饰器 > 方法装饰器 > 方法参数装饰器 > 类装饰器,同一类型装饰器有多个则是从后往前执行。
Javascript类型检查
TS在2.3之后版本可以使用 --checkJs 对 .js 文件进行类型检查和错误提示。
可以通过添加注释来控制检查规则,部分规则如下:
// @ts-nocheck 忽略类型检查
// @ts-check 则检查某些.js文件
// @ts-ignore 忽略本行错误
https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html