TypeScript

TypeScript

参考链接

typescript中文手册open in new window

AST抽象语法树open in new window

在线开发 TypeScriptopen in new window

安装 TypeScript

  • 命令行运行如下命令,全局安装 TypeScript
npm install -g typescript
  • 安装完成后,在控制台运行如下命令,检查安装是否成功(Version 4.4.3):
tsc -v 
  • 运行单独ts文件
ts-node  [文件名.ts]
  • 将ts转换为js文件
tsc  [文件名.ts]

tsc --init 新建tsconfig.json配置文件

基础类型

布尔值 数字 字符串 数组 元组 枚举 基础类型

原始类型

const a:string='foobar'
const b:number=Infinity
const d:boolean=false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()

数组类型

const arr1: Array<number> = [1, 2, 3]//纯数字组成
const arr2: number[] = [1, 2, 3]//常见
//元组属于数组的一种,元组中的元素可以不必全部保持类型一致
const foo: [number, string] = [1, 'foo']

对象类型

let obj: { foo: String, bar: number } = { foo: 'string', bar: 100 }//必须有foo/bar 数据类型正确
let obj: { foo?: String, bar: number } = { bar: 100 }//foo可有可无
let obj3:{ [string]: string }
obj3.key1 = 'value123'
obj3.key2 = 100//false

函数类型

 function sum(a: number, n?: number) {
     return a + b
 }
 sum(10, 20)
 sum(10, '20')
// 回调参数类型限制 
//n?: number加了问号是可选参数
//参数 age:number=20为默认参数
//void 类型表示没有任何类型,一般用于定义方法的时候方法没有返回值。
function foo(callback: (string, number) => void)) {
    callback('string', 100)
}
// 剩余参数 在参数的类型确定而参数个数不确定的情况时,我们需要用到剩余参数,
//它使用 ...将接收到的参数传到一个指定类型的数组中.剩余参数必须配置到参数的最后面。
function sum(one:string,...result:number[]):number{
    console.log(one,...result);
    return 123
}
sum('123',1,2,2,2,2,3,5,5)
//接受任意参数
...rest:number[]

特殊类型

const type: 'success' | 'warn' | 'danger' = 'success'
type StringOrNumber = string | Number
const b: StringOrNumber = 'string' //10

mixed/any类型接收任意类型
//any弱类型 mixed强类型
// never类型,永不存在的值类型
//never是任何类型的子类型,可以赋值任何类型。但是没有类型是 never 的子类型或可以赋值给 never 类型,
// 即使 any 类型也不可以赋值给never。这意味着声明 never 类型的变量只能被 never 类型所赋值。
function error(): never {
    throw new Error('抛出错误了');
}
var test = (): never => {//永远拿不到返回值
  while (true) {}
};

枚举类型

给一组数值取更好的名字。数据中只会出现固定的值

enum 枚举名 {
    标识符[= 整型常数/字符串],
    标识符[= 整型常数/字符串], 
    ...
    标识符[= 整型常数/字符串],
};

enum flag{
    pedding,
    succes='up',//字符串枚举之后,需要给后面参数赋值
    error=888,
    loading='456'
}
console.log(flag.pedding);//0
console.log(flag.succes);//up
console.log(flag.error);//888
console.log(flag.loading);//889
//如果标识符没有赋值,它的值就是下标。

enum Color {
  red,
  blue,
  green,
}

let color: Color = Color.red;

Object类型

  • 除了原始类型的其他类型
function getObj(options: object|string): object {//联合类型
  return {};
}
console.log(getObj({ xxx: 123 }));

断言

  • 类型断言: 可以用来手动指定一个值的类型
  • 类型推断: TS会在没有明确的指定类型的时候推测出一个类型 有下面2种情况:
    1. 定义变量时赋值了, 推断为对应的类型
    2. 定义变量时没有赋值, 推断为any类型
/* 
语法:
    方式一: <类型>值
    方式二: 值 as 类型  tsx中只能用这种方式
*/
let nums=[110,200,300]
let res=nums.find(i=>i>50)//res可能是number|undefind
let num1=res as number //断言他是number类型,并非转换
let num2=<number>res //jsx不可使用,与标签冲突

接口

(一种规范,约定结构)最好约束一个规范,约束对象或者函数的类型

//定义接口
interface Post {
  title: string;
  content: number;
  subTitle?: string;//可选成员
  readonly task: string; //只读成员
}
//定义函数时掉用接口
function printPost(query: Post) {
  console.log(query.title);
  console.log(query.content);
}
//调用函数时 需要符合接口规范,参数&类型必须一样
printPost({
  title: "学习ts",
  content: 789,
});
//定义可选参数接口
interface myGo {
  [abckey: string]: string; //第一个string是key,第二个是value
}
var obj: myGo = {};
obj.lol = "英雄联盟";
obj.dota = "刀塔";
obj.run = 456; //报错
console.log(obj);

  • 用来描述一类具体对象的抽象成员
    • public:公有类型,在当前类里面、子类、类外面都可以访问 (默认)

    • protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问

    • private:私有类型,在当前类里面可以访问,子类、类外部都没法访问

    • static : 静态方法,使用的时候类可以访问,比如Promise.all([])

    • readonly 只读属性 无法修改

//基本使用
class Person {
  name: string;//属性,前面省略了public关键词
  private sex: string;//属性,前面省略了public关键词
  constructor(name: string, sex: string) {//构造函数,实例化类的时候触发的方法
    this.name = name;
    this.sex = sex;
  }
  personcan(can: string): void {
    console.log(`这个${this.name}${this.sex}的,可以去${can}`);
  }
}
var P1 = new Person("小名", "男");
console.log(P1.name);
P1.personcan("吃饭");

类与接口

// 类与接口
interface Eat {
  eat(food: string): void;
}
interface Run {
  run(distance: number): void;
}
// 定义类  接口约束 逗号隔开 implements后是接口约束
class Person implements Eat, Run {
  eat(food: string): void {
    console.log("优雅永不过时的吃" + food);
  }
  run(distance: number): void {
    console.log("站着走" + distance);
  }
}

接口继承接口

interface Person extends eat, study {

}

abstract抽象类

  • 约束子类当中必须有某一成员
//定义抽象类
abstract class Animal {
  eat(food: string): void {
    console.log("动物都会吃", food);
  }
  //方法“run”不能具有实现,因为它标记为抽象(只定义不写函数)
  abstract run(distance: number): void;
  //当用了Animal抽象类之后,必须要实现run这个方法
}
class Pig extends Animal {
  run(distance: number): void {
    console.log("随便走", distance);
  }
}
var pg = new Pig();
pg.eat("吃");
pg.run(566);

继承

extends 关键字

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
class Student extends Person {
  constructor(name: string) {
    // 调用父类型构造方法
    super(name);
  }
  go() {
    // 调用父类型的一般方法
    // super.run(distance);
  }
}

get|set

  • 通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
class Person {
  firstName: string = "常山";
  lastName: string = "赵子龙";
  get fullName() {
    //获取时触发
    return this.firstName + "---" + this.lastName;
  }
  set fullName(value) {
    //修改时触发
    const names = value.split("---");
    this.firstName = names[0];
    this.lastName = names[1];
  }
}

const p = new Person();
console.log(p.fullName);

p.firstName = "咏春";
p.lastName = "叶问";
console.log(p.fullName);

p.fullName = "篮球---蔡徐坤";
console.log(p.firstName, p.lastName);

多态

子类重写父类的同名方法

函数重载

同一个类里面,函数名字是一样的,但是参数有差别(类型||个数),就是函数的重载

函数签名 = 函数名称+函数参数+函数参数类型+返回值类型四者合成,包含了实现签名和重载签名 function funa(value: number, type: string): string 这就是函数签名(重载签名)

函数重载由一个实现签名 + 一个或多个重载签名合成

参考:https://www.jianshu.com/p/98a4291d7ff4

函数重写

发生在父子类里面,方法名和参数也一样

泛型

泛型是指附属于函数、类、接口、类型别名之上的类型,当某个函数的参数,返回值和内部使用时的类型无法确定的情况下,就可以使用泛型来进行约束

//泛型 声明这个函数、接口或类的时候不指定具体类型 调用在指定
function createArray<T>(length: number, value: T): T[] {
  return Array<T>(length).fill(value);
}
//型类型“T”就像一个参数,可供传递,如我们在调用函数时在尖括号中传入,则这个T(泛型)的类型就是number,
//这样我们就可以any类型都换成泛型就可以了,这样就当泛型参数传入number类型时,函数的返回值就是number类型的数组
console.log(createArray<string>(25, "das"));
console.log(createArray<number>(25, 1326));

类型声明模块declare

import { camelase } from "lodash";
declare function camelase(params: string): string;

Iterators 和 Generators

迭代器和生成器

装饰器

概念

它是一个表达式,该表达式被执行后,返回一个函数,函数的入参分别为 target、name 和 descriptor ,执行该函数后,可能返回 descriptor 对象,用于配置 target 对象

装饰器的分类

类装饰器(Class decorators)

// 类装饰器
function Log(target: any) {
console.log(target);
console.log("in log decorator");
}

@Log
class A {
constructor() {
  console.log("constructor");
  // logger.log('ddd');
}
}
// new A();类装饰器在没new对象的时候,js就已经执行了类装饰器的方法

属性装饰器(Property decorators)

定义属性装饰器,用来装饰类的属性。它接收两个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 被装饰类的属性名
function logProperty(params: any) {
  // target--->类的原型对象;attr--->传入的参数url
  return function(target: any, attr: any) {
    console.log("属性装饰器;", target, attr);
    target[attr] = params;
  };
}

class HttpClient {
  @logProperty("http://www.baidu.com")
  public url: any | undefined;
  constructor() {}
  getUrl() {
    console.log("getUrl", this.url);
  }
}

console.log(new HttpClient().getUrl());

方法装饰器(Method decorators)

用来装饰类的方法。它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • descriptor: TypePropertyDescript - 属性描述符

方法的装饰

// 方法装饰器
function GET(url: string) {
  console.log("entry GET decorator");
  return function(
    target: any,
    methodName: string,
    descriptor: PropertyDescriptor
  ) {
    console.log("entry GET decorator function");
    !target.$Meta && (target.$Meta = {});
    target.$Meta[methodName] = url;
  };
}

class HelloService {
  constructor() {
    console.log("constructor");
  }
  //使用
  @GET("xx")
  getUser() {
    console.log("getUser function called");
  }
}
new HelloService().getUser();
// console.log((<any>HelloService).$Meta);
// console.log((new HelloService() as any).$Meta);

参数装饰器(Parameter decorators)

装饰函数参数,它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • parameterIndex: number - 方法中参数的索引值
function PathParam(paramName: string) {
  return function(target: any, methodName: string, paramIndex: number) {
    !target.$Meta && (target.$Meta = {});
    target.$Meta[paramIndex] = paramName;
  };
}

class HelloService {
  constructor() {}
  getUser(@PathParam("userId") userId: string) {}
}

console.log((<any>HelloService).prototype.$Meta); // {'0':'userId'}

编译原理

Scanner 扫描仪 Parser 解析器 Binder 绑定器 Checker 检查器 Emitter 发射器 ts编译原理

面试题

类型推论 & 可赋值性

类型推论:没给标志,ts系统推导出来的,声明的应该是什么类型

可赋值性:数组,布尔,数字,对象,函数,类、字符串,字面量类型,满足以下任⼀条件时,A类型可以赋值给B类型。

  • A是B的⼦类型
  • A是any类型

什么是类型断言?

可以用来手动指定一个值的类型,定义变量时赋值了, 推断为对应的类型

  • 方式一: <类型>值
  • 方式二: 值 as 类型 tsx中只能用这种方式

缺点:运行时可能才报错

type 和 interface的异同

  • type(类型别名)侧重于描述类型
  • interface侧重于描述数据结构,这个结构该有哪些类型的变量

相同点:

  • 都可以描述⼀个对象或者函数
  • interface和type都可以拓展,interface可以extends type, type也可以extends interface. 效果差不多,语法不同。

不同点:

  • 类型别名可以⽤于其它类型 (联合类型、元组类型、基本类型(原始值)),interface不⽀持
  • interface 可以多次定义 并被视为合并所有声明成员 type 不⽀持
  • type 能使⽤ in 关键字⽣成映射类型,但 interface 不⾏。

有哪些类型装饰器?执行的顺序是怎样的?

类装饰器、属性装饰器、方法装饰器、参数装饰器

执行顺序

  • 有多个参数装饰器时:从最后⼀个参数依次向前执⾏
  • ⽅法和⽅法参数中参数装饰器先执⾏。
  • 类装饰器总是最后执⾏。
  • ⽅法和属性装饰器,谁在前⾯谁先执⾏。因为参数属于⽅法⼀部分,所以参数会⼀直紧紧挨着⽅法 执⾏。

接口类型有哪些种类

属性类接口 函数类接口 可索引接口 类类型接口 扩展接口

TypeScript和JavaScript的区别

  • 更规范,ts是js的超集,即你可以在ts中使用原生js语法。
  • ts在开发时能给出编译错误,js是弱类型语言需要运行时暴露
  • ts为强类型语言,代码可读性强,不允许类型随意转换,对值所具有的结构进行类型检查
  • TypeScript是面向对象的编程语言,提供了类、模块和接口,更易于构建组件和维护