基础类型定义

string/number/boolean/null/undefined/void/symbol

  1. const num: number = 123
  2. const name : string = "typescrit"

字面量

字面量的意思就是直接声明, 而非new关键词实例化出来的数据。

  1. // 字面量
  2. const n:number = 123;
  3. const s:string = '456';
  4. const o:object = {a:1,b:'2'};
  5. // 非字面量
  6. const n:Number = new Number(123);
  7. const s:String = new String('456');
  8. const o:Object = new Object({a:1,b:'2'});

通过上面的例子, 大家应该看明白为什么ts中有些类型的开头字母是小写的。这是因为ts中用小写字母开头的类型代表字面量, 大写的是用来表示通过new实例化的数据。

类型的内容是固定的

  1. type defType = 'a' | 'b' | 'c' | 'string';
  2. let type1 : defType = 'b';
  3. let type1 : defType = 'a';

类型定义

基本类型定义

  1. const boy:{
  2. name:string;
  3. age:number;
  4. // 索引签名
  5. [props:sting]:string;
  6. } = {
  7. name:"tom",
  8. age: 12
  9. }

定义数组类型

  1. const numArray: number[] = [1,2,3]
  2. const strArray: Array<string> = ["str1", "str2"]

定义函数,约束参数类型

  1. const getTotal:() => number = () =>{ return 123; }
  2. // 定义函数类型的接口
  3. interface SearchFunc{
  4. (source:string, subString:string) : boolean;
  5. }
  6. let search:SearchFunc;
  7. mySearch = function(source: string, substring:string):boolean{
  8. let result = source.search(substring)
  9. return result > -1
  10. }

定义接口

接口可以实现一个对象包含多个类型。

  1. interface IBoy {
  2. name:sting;
  3. age: number;
  4. }
  5. let xiaoming:IBoy ={
  6. name: "xiaoming",
  7. age: 16
  8. }

类实现接口,约束实例属性和方法

  1. // 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口
  2. interface AnimalInterface {
  3. name: string;
  4. legs: number;
  5. wings?: number;
  6. call(sound: string): void;
  7. }
  8. // 类实现接口,类就必须有接口的属性和方法
  9. class Dog implements AnimalInterface {
  10. legs: number = 0;
  11. constructor(public name: string, legs: number) {
  12. this.name = name;
  13. this.legs = legs;
  14. }
  15. call(sound: string): void {
  16. sound = '旺 旺 旺'
  17. }
  18. }
  19. class Bird implements AnimalInterface {
  20. constructor(public name: string, public legs: number, public wings: number) {
  21. this.legs = legs;
  22. this.wings = wings;
  23. }
  24. call(sound: string): void {
  25. sound = '唧 唧 唧 唧'
  26. }
  27. }
  28. let wangcai = new Dog("旺财", 4)
  29. let bage = new Bird("八哥", 2, 2)

类约束静态属性和方法,定义构造器接口

  1. // 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口
  2. interface AnimalInterface {
  3. name: string;
  4. legs: number;
  5. wings?: number;
  6. call(sound: string): void;
  7. }
  8. // 定义构造器接口
  9. interface AnimalConstructorInterface {
  10. new(name: string, legs: number, wings?:number): AnimalInterface;
  11. }
  12. // 创建动物工厂方法
  13. function createAnimal(constru:AnimalConstructorInterface,name: string, legs: number, wings?:number): AnimalInterface{
  14. // 这里就可以约束构造器的属性
  15. return new constru(name, legs, wings);
  16. }
  17. // 类实现接口,类就必须有接口的属性和方法
  18. class Dog implements AnimalInterface {
  19. legs: number = 0;
  20. constructor(public name: string, legs: number) {
  21. this.name = name;
  22. this.legs = legs;
  23. }
  24. call(sound: string): void {
  25. sound = '旺 旺 旺'
  26. }
  27. }
  28. class Bird implements AnimalInterface {
  29. name:string = 'Bird';
  30. legs:number = 0;
  31. // 对构造器的属性会进行强制约束
  32. constructor() {
  33. }
  34. call(sound: string): void {
  35. sound = '唧 唧 唧 唧'
  36. }
  37. }
  38. let wangcai = createAnimal(Dog, "旺财", 4)
  39. let bage = createAnimal(Bird, "八哥", 2, 2)

接口和接口的继承

  1. interface Shape{
  2. color:string;
  3. }
  4. interface Points{
  5. num:number;
  6. size:number;
  7. }
  8. interface Square extends Shape, Points{
  9. area: number
  10. }
  11. // let s = {} as Square
  12. let s = <Square>{}
  13. s.color = 'red'
  14. s.num = 3;

接口和类的继承

  1. class Component {
  2. private state:string;
  3. }
  4. interface SelectableComponent extends Component{
  5. select():void;
  6. }
  7. class ButtonComponent extends Component implements SelectableComponent{
  8. // ButtonComponent实现了SelectableComponent接口,则必须实现接口中的方法。
  9. select():void{}
  10. }
  11. class TextComponent extends Component{
  12. select():void{}
  13. }
  14. // InputComponent出现类型错误,InputComponent没有继承Component,此处没继承到state属性,会出现错误
  15. class InputComponent implements SelectableComponent{
  16. select():void{}
  17. }

函数参数是对象

  1. function add({ first = 1, second = [0] }: { first?: number; second?: number[]; }): number {
  2. return first + second.length;
  3. }
  4. console.log(add({ first: 1, second: [1, 2, 3] })); //4

TS类型保护机制

有时候在代码中多处添加类型断言,明确告知TS编译器的类型,会造成代码可读性低,增加维护难度。可以使用类型保护机制。有以下四种方法

  1. // 共用的变量
  2. class Java {
  3. helloJava() {
  4. console.log("Hello Java")
  5. }
  6. }
  7. class JavaScript {
  8. helloJavaScript() {
  9. console.log("Hello JavaScript")
  10. }
  11. }
  12. // ts类型保护
  13. enum Type { Strong, Week };
  14. function getLanguage(type: Type, x: string | number = "default") {
  15. let lang = type === Type.Strong ? new Java() : new JavaScript()
  16. // ts中报错,无法识别属性helloJava
  17. if(lang.helloJava){
  18. lang.helloJava()
  19. }else{
  20. lang.helloJavaScript()
  21. }
  22. return lang
  23. }

一:使用instanceof关键字创建区块

  1. function getLanguage(type: Type, x: string | number = "default") {
  2. let lang = type === Type.Strong ? new Java() : new JavaScript()
  3. if(lang instanceof Java) {
  4. lang.helloJava()
  5. }else{
  6. lang.helloJavaScript()
  7. }
  8. return lang
  9. }

二:使用in 关键字

  1. function getLanguage(type: Type, x: string | number = "default") {
  2. let lang = type === Type.Strong ? new Java() : new JavaScript()
  3. if("helloJava" in lang) {
  4. lang.helloJava()
  5. }else{
  6. lang.helloJavaScript()
  7. }
  8. return lang
  9. }

三:使用typeof 判断变量的基本类型

  1. function getLanguage(type: Type, x: string | number = "default") {
  2. let lang = type === Type.Strong ? new Java() : new JavaScript()
  3. if(typeof x ==="string"){
  4. x = x.toUpperCase()
  5. }else{
  6. x = x.toFixed(2)
  7. }
  8. return { lang, x }
  9. }

四:使用类型保护函数

类型保护函数:返回值为类型谓词,格式: 参数 + is + 类型

  1. function isJava(lang: Java | JavaScript): lang is Java{
  2. return (lang as Java).helloJava !== undefined
  3. }
  4. function getLanguage(type: Type, x: string | number = "default") {
  5. let lang = type === Type.Strong ? new Java() : new JavaScript()
  6. if(isJava(lang)){
  7. lang.helloJava()
  8. }else{
  9. lang.helloJavaScript()
  10. }
  11. return lang
  12. }

TS的高级类型-交叉类型

将多个类型合并成为一个新的类型,新类型具有所有类型的特性
语法:typeA & typeB
多用于扩展类型的属性

  1. // 交叉类型
  2. interface InterfaceA{
  3. methodA(): void;
  4. }
  5. interface InterfaceB{
  6. methodB(): void;
  7. }
  8. let aAndb:InterfaceA & InterfaceB = {
  9. methodA(){},
  10. methodB(){}
  11. }
  12. console.log(aAndb)

扩展类型的属性

  1. function mixin<T extends object, K extends object>(o1:T, o2:U):T&U{
  2. return {...o1, ...o2}
  3. }
  4. let r = mixin({name:'北鸟南游', age: 3}, {address:"shanghai"})

TS的高级类型-联合类型

  • ts中使用 ! 表示非空断言,表示该值一定不会为空
  • ts中用 ?. 链式判断操作符,表示对象存在的情况下,获取对象的属性,如果对象不存在,则进行获取属性
  • ts中 ?? 表示第一个值非null或undefined,就取第一个值。用来排除null和undefined ```typescript // ! 表示值一定存在, ele不会为null let ele: HTMLElement |null = document.getElementById(“app”) ele!.innerHTML = “hello world”

// ?.的使用,aa && aa.xx && aa.xx.yy let a = {b: ‘b’} console.log(a?.b?.c)

// ??使用 123 ?? ‘save’ // 123 null ?? ‘save’ // save

  1. <a name="atF5W"></a>
  2. #### 联合类型
  3. ```typescript
  4. interface JavaInterface {
  5. helloJava(): void;
  6. build(): void;
  7. }
  8. interface JsInterface {
  9. helloJavaScript(): void;
  10. build(): void;
  11. }
  12. class Java implements JavaInterface {
  13. static Strong:boolean = true;
  14. helloJava() {
  15. console.log("Hello Java")
  16. }
  17. build(){
  18. console.log("build")
  19. }
  20. }
  21. class JavaScript implements JsInterface {
  22. static Strong:boolean = false;
  23. helloJavaScript() {
  24. console.log("Hello JavaScript")
  25. }
  26. build(){
  27. console.log("build")
  28. }
  29. }
  30. // ts类型保护
  31. enum Type { Strong, Week };
  32. function getLanguage(type: Type):JavaInterface|JsInterface {
  33. let lang = type === Type.Strong ? new Java() : new JavaScript();
  34. return lang
  35. }
  36. // getLanguage方法中lang对象为联合类型,此时lang的类型不能确定是Java还是JavaScript
  37. // 只能使用两个对象共有的属性/方法,如:build();
  38. // 此时调用helloJava或helloJavaScript则报错

可区分的联合类型

如果函数参数的类型是多个类型的联合类型,且多个类型间有一个共用属性,可以利用这个共用属性,创建出不同的类型保护区块

  1. interface Square {
  2. kind: "Square";
  3. side: number;
  4. }
  5. interface Rectangle {
  6. kind: "Rectangle";
  7. width: number;
  8. height: number;
  9. }
  10. interface Circle{
  11. kind: "Circle";
  12. radius: number;
  13. }
  14. type Shape = Square | Rectangle | Circle;
  15. // 由于此时没有设置Circle类型的判断,如果s为Circle类型则会返回undefined。
  16. // TS报错:Function lacks ending return statement and return type does not include 'undefined'.
  17. function area(s: Shape): number {
  18. switch (s.kind){
  19. case 'Square':
  20. return s.side * s.side
  21. case 'Rectangle':
  22. return s.height * s.width
  23. }
  24. }

类型never类型,在switch中,将未捕获到的类型赋值给never,检查s是否是never类型。

  1. function area(s: Shape) {
  2. switch (s.kind){
  3. case 'Square':
  4. return s.side * s.side
  5. case 'Rectangle':
  6. return s.height * s.width
  7. // 当缺少对Circle类型判断时,说明可以进入到default,s是Circle类型,并不是设置的never类型
  8. // TS报错:Argument of type 'Circle' is not assignable to parameter of type 'never'.
  9. default:
  10. return ((e: never) => {
  11. throw new Error(e)
  12. })(s)
  13. }
  14. }

正确的设置,switch分支对联合类型的所有类型都进行覆盖

  1. function area(s: Shape) {
  2. switch (s.kind){
  3. case 'Square':
  4. return s.side * s.side;
  5. case 'Rectangle':
  6. return s.height * s.width;
  7. case 'Circle':
  8. return (s.radius**2) * Math.PI;
  9. default:
  10. return ((e: never) => {
  11. throw new Error(e)
  12. })(s)
  13. }
  14. }

TS的高级类型-索引类型

索引类型三个概念:

  • 索引类型查询操作符
    • keyof T:表示类型T,所有公共属性字面量的联合类型
  • 索引类型访问操作符
    • T[K]:表示对象T的属性K所表示的类型
  • 范型约束
    • T extends U:表示范型变量可以通过继承某个类型,获得属性

      索引类型查询

      TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称。keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
      keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键。
      除了接口外,keyof 也可以用于操作类
      keyof T:表示类型T的所有属性的字面量的联合类型 ```typescript // 定义一个接口Obj含有属性a,b interface obj { age: number name: string } // 定义变量key,类型为keyof Obj —> “age” | “name” let key: keyof obj = “name” // key的类型为 “age” | “name”

// keyof操作类 class Person { name: string = “tom”; }

let sname: keyof Person; sname = “name”;

//keyof 操作对象 type A = keyof {a:1,b:’123’} // ‘a’|’b type B = keyof [1,2] // ‘0’|’1’|’push’…, 获取到内容的同时,还得到了Array原型上的方法和属性

  1. keyof 操作符除了支持接口和类之外,它也支持基本数据类型:
  2. ```typescript
  3. let K1: keyof boolean; // let K1: "valueOf"
  4. let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
  5. let K3: keyof symbol; // let K1: "valueOf"

另外一种特殊情况,定义的接口属性不定。

  1. interface Foo {
  2. [props: string]: string;
  3. }

此时Foo接口被认定为属性字段全部为string,但是由于js可以通过数字和字符串访问对象属性,因此keyof Foo的结果是string | number;

  1. let f:key Foo = 123; //此时也不报错

image.png

如果想通过一个方法getKey获取对象里的属性值,通常的写法

  1. let Boy = {
  2. name:"xm",
  3. age:10
  4. }
  5. function getKeys(o:object, k: string){
  6. return o[k];
  7. }

此时在return o[k]时,TS会报错,类型检验无法通过。
image.png
为了解决这个错误,可以使用范型加索引类型

  1. let Boy = {
  2. name:"xm",
  3. age:10
  4. }
  5. function getKeys<T extends object, K extends keyof T>(o:T, name:K): T[K]{
  6. return o[name];
  7. }
  8. console.log(getKeys(Boy, "name"));

索引类型访问

语法:T[K],表示对象T的属性K的类型

  1. interface Person {
  2. age: number;
  3. }
  4. let p:Person['age']; //p:number,p的类型为数字
  5. type Man = { name:'man'; age:12}
  6. let m:Man['name']; //m的类型为string

范型约束

T extends U:范型T可以继承对象U的属性

  1. function prop<T extends object, K extends keyof T>(obj: T, keys: K): T[K]{
  2. return obj[keys];
  3. }

TS的高级类型-映射类型

映射类型:TS允许将一个类型映射成另外一种类型,有以下Readonly、Partial、Pick三种同源映射,和Record非同源映射。

Readonly 只读

将一个接口的所有属性映射为只读

  1. interface Obj{
  2. a:number;
  3. b:string;
  4. c:boolean;
  5. }
  6. type ReadonlyObj = Readonly<Obj>
  7. /* TS编译之后
  8. type ReadonlyObj = {
  9. readonly a: number;
  10. readonly b: string;
  11. readonly c: boolean;
  12. } */
  13. let ReadonlyObjDemo:ReadonlyObj = {
  14. a:1,
  15. b:"readonly",
  16. c:true
  17. }
  18. ReadonlyObjDemo.a = 3;
  19. //error Cannot assign to 'a' because it is a read-only property.

Partical 可选

将一个接口的所有属性映射为可选

  1. type PartialObj = Partial<Obj>
  2. /* TS编译之后
  3. type PartialObj = {
  4. a?: number | undefined;
  5. b?: string | undefined;
  6. c?: boolean | undefined;
  7. } */
  8. // a/b/c三个属性都是可选的
  9. let PartialObjDemo:PartialObj = {
  10. a:100
  11. }

Pick 摘取部分

摘取对象的一部分属性,形成新类型

  1. type PickObj = Pick<Obj, "a" | "b"> //抽取了Obj对象的a、b属性,形成一个新的类型
  2. /* TS编译之后
  3. type PickObj = {
  4. a: number;
  5. b: string;
  6. } */
  7. let pickObjDemo:PickObj = {
  8. a:66,
  9. b:'66'
  10. }

非同源映射Record

引入新属性,将引入的属性的类型,设置为第二个参数。

  1. /*
  2. Record 源码
  3. type Record<K extends keyof any, T> = {
  4. [P in K]: T;
  5. };
  6. */
  7. // K为新增的属性,T为属性类型
  8. type RecordObj = Record<"x" | "y", Obj>
  9. // 定义一个对象包含x/y属性,这两个属性的类型为Obj类型
  10. /* TS编译之后
  11. type RecordObj = {
  12. x: Obj;
  13. y: Obj;
  14. } */
  15. let RecordObjDemo:RecordObj = {
  16. x:{
  17. a:1,
  18. b:"x",
  19. c:true
  20. },
  21. y:{
  22. a:2,
  23. b:"y",
  24. c:false
  25. },
  26. }

TS的高级类型-条件类型

TS提供了几种内置的预定义的条件类型

  • Exclude - 用于从类型T中去除不在U类型中的成员
  • Extract - 用于从类型T中取出可分配给U类型的成员
  • NonNullable - 用于从类型T中去除undefined和null类型
  • ReturnType - 获取函数类型的返回类型
  • InstanceType - 获取构造函数的实例类型

    条件类型

    条件类型是由条件表达式决定的类型,使类型具有不唯一性,增加TS语言灵活性。
    T extends U ? X : Y;
    如果类型T可被赋值给类型U,那么结果类型是X类型,否则是Y类型。 ```typescript // 条件类型 type TypeName = T extends string ? ‘string’ : T extends number ? ‘number’ : T extends boolean ? ‘boolean’ : T extends undefined ? ‘undefined’ : T extends Function ? ‘Function’ : ‘object’

// 定义类型T1为条件类型,传入参数string,指定t1为string类型 type T1 = TypeName // 定义类型T2为条件类型,传入参数string[] type T2 = TypeName

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/737887/1622537171661-74e0384c-8456-4701-b448-71700cb87498.png#clientId=u150ec533-a6fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=104&id=uc5422fee&margin=%5Bobject%20Object%5D&name=image.png&originHeight=104&originWidth=395&originalType=binary&ratio=1&rotation=0&showTitle=false&size=10980&status=done&style=none&taskId=uf2a6b497-2d85-4571-bbaf-0d1aa6c3b95&title=&width=395)<br />T2传入的是数组,不在所属的条件中,则T2的类型为最后的object。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/737887/1622537204681-f0934506-efe5-4d37-ac48-13ac73725ca7.png#clientId=u150ec533-a6fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=110&id=uad7eae1e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=110&originWidth=420&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13696&status=done&style=none&taskId=u2882d473-f9d0-4552-b1ff-4a7a6b511b6&title=&width=420)
  2. <a name="WSEBI"></a>
  3. #### 分步式条件类型
  4. T是联合类型,结果类型变为多条件类型的联合类型<br />(A | B) extends U ? X : Y;<br />可以将AB拆解<br />(A extends U ? X : Y) | (B extends U ? X : Y)
  5. 这样定义的变量会被推断为联合类型
  6. ```typescript
  7. type T3 = TypeName<string | string[]>

分步式条件类型应用

主要应用是对类型进行过滤
如果T可以被赋值给U,结果类型为never类型,否则为T类型

  1. // 如果T可以被赋值给U,结果类型为never类型,否则为T类型
  2. type Diff<T, U> = T extends U ? never : T
  3. type T4 = Diff<'a' | 'b' | 'c', 'a' | 'x'>

image.png
拆解逻辑分析
Diff会被拆解为多个条件类型的联合类型

  1. type Diff<T, U> = T extends U ? never : T
  2. type T4 = Diff<'a' | 'b' | 'c', 'a' | 'e'>
  3. // 拆解分析:
  4. // Diff<'a', 'a' | 'e'> | Diff<'b', 'a' | 'e'> | Diff<'c', 'a' | 'e'>
  5. // never | 'b' | 'c'
  6. // 'b' | 'c'
  1. 先判断a是否可以被赋值给这个字面量联合类型’a’ | ‘e’,答案是可以的,所以返回never
  2. 继续,因为b不可以被赋值给字面量联合类型’a’ | ‘e’,所以返回b
  3. 继续,c不可以被赋值给’a’ | ‘e’,所以返回c
  4. 最后,never和b,c的联合类型为’b’ | ‘c’

可以实现从类型T中移除不需要的类型,如undefined和null,定义一个NotNull,从T中过滤掉undefined和null

  1. // Diff扩展:从T中过滤掉undefined和null
  2. type NotNull<T> = Diff<T, undefined | null>
  3. // 过滤掉undefined和null,T5的类型就变成了string和number
  4. type T5 = NotNull<string | number | undefined | null>

image.png
上述实现过程,在TS中有内置的方法:Extract和Exclude和NonNullable以及ReturnType和InstanceType;
Exclude作用是从类型T中过滤掉可以赋值给类型U的类型
Extract作用是可以从类型T中抽取出可以赋值给U的类型

  1. // Extract<T, u>和Exclude<T, U>
  2. type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>
  3. type T7 = Exclude<'a' | 'b' | 'c', 'a' | 'e'>

image.pngimage.png
TS中Extract和Exclude的源码分析

  1. /**
  2. * Exclude from T those types that are assignable to U
  3. */
  4. type Exclude<T, U> = T extends U ? never : T;
  5. /**
  6. * Extract from T those types that are assignable to U
  7. */
  8. type Extract<T, U> = T extends U ? T : never;

NonNullable,排除掉所有空的类型

  1. // NonNullable,排除掉所有空的类型
  2. type T9 =NonNullable<string | number | undefined | null>

image.png
NonNullable源码

  1. /**
  2. * Exclude null and undefined from T
  3. */
  4. type NonNullable<T> = T extends null | undefined ? never : T;

ReturnType可以获取一个函数返回值的类型

  1. // ReturnType<T>可以获取一个函数返回值的类型
  2. type T8 = ReturnType<() => string>

image.png
TS中ReturnType源码:

  1. /**
  2. * Obtain the return type of a function type
  3. */
  4. type ReturnType<T extends (...args: any) => any>
  5. = T extends (...args: any) => infer R ? R : any;

实现分析:
T extends (…args: any) => any:ReturnType要求参数T可以赋值给一个函数,这个函数有任意的参数,返回值类型也是任意的
由于函数返回值类型不确定,这里使用了infer关键字,表示待推断,延迟推断,需要根据实际的情况确定
infer R ? R : any:
如果实际类型是R,那么结果类型就是R,否则返回值类型就是any
InstanceType:返回构造函数的类型

  1. class CDemo {
  2. constructor(x:number,y:string){}
  3. }
  4. type CType = InstanceType<typeof CDemo>

image.png
TypeScript源码的实现

  1. /**
  2. * Obtain the return type of a constructor function type
  3. */
  4. type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;

对象的空值合并运算符

有时,在访问属性时 TypeScript 会自动帮你插入可选链操作符。类似 let t = myObj?.property,这样的话变量 t 将被赋值为 propertyundefined

  1. let t = myObj?.text ?? " "

如果myObj下没有属性text则会返回undefined,然而有些情况不希望返回的是undefined,可以用??设置默认值。
?. 可选链操作符
?? 空值合并运算符

数组和元组

数组

  1. const arr:(number | string)[] = [1,'a',2]
  2. const stringArr:string[] = ['a', 'b', 'c']

数组存储对象类型

  1. // type alias 类型别名
  2. type student = {
  3. name:string,
  4. age:number
  5. }
  6. const objArr:student[] = [{
  7. name: 'ts',
  8. age:12
  9. }]

元组 tuple

  1. const teacher:[string, string, number] = ['chinese','grey',21]

接口 interface

出现重复定义的类型判断

  1. const getName = (person: {name:string}): string => {
  2. return person.name;
  3. };
  4. const setName = (person: {name:string}, name: string): void => {
  5. person.name = name;
  6. };

上面代码重复出现name:string的定义。可以定义成接口

  1. interface IPerson {
  2. name: string;
  3. }
  4. const getName = (person: IPerson): string => {
  5. return person.name;
  6. };
  7. const setName = (person: IPerson, name: string): void => {
  8. person.name = name;
  9. };
  • 接口中使用?定义可选参数
  • 使用readonly定义只读参数 ```typescript interface IPerson { name: string; readonly gender: string; age? : number; }

const getName = (person: IPerson): string => { return person.name; };

  1. <a name="6c8d5cc957d0eae4a6bc228dc913ce01"></a>
  2. ### 定义一些新增属性
  3. ```typescript
  4. interface IPerson {
  5. name: string;
  6. age: number;
  7. [props: string]: any;
  8. }
  9. const getName = (person: IPerson): string => {
  10. return person.name + person.age;
  11. };
  12. getName({
  13. name: "ts",
  14. age: 1,
  15. other: true,
  16. });

接口定义方法

  1. interface IPerson{
  2. name:string;
  3. say():string;
  4. }
  5. const getName = (person: IPerson): string => {
  6. return person.name;
  7. };
  8. getName({
  9. name:'ts',
  10. say(){
  11. return 'hello ts!'
  12. }
  13. })

类class 实现接口

  1. interface IPerson{
  2. name:string;
  3. say():string;
  4. }
  5. class Student implements IPerson{
  6. public name:string;
  7. say(){
  8. return "我叫:" + this.name;
  9. }
  10. }

接口的继承

  1. interface IPerson{
  2. name:string
  3. }
  4. interface IStudent extends IPerson {
  5. grade: string;
  6. }
  7. const xiaoming: IStudent = {
  8. name: "xiaoming",
  9. grade: "五年级"
  10. };

函数Function

函数的参数类型定义

  1. const add=(x:number, y:number, z?:number): void =>{
  2. if(typeof z=== 'number'){
  3. return x+y+z;
  4. }else{
  5. return x + y
  6. }
  7. }
  8. interface ISum{
  9. (x:number, y:number, z?:number):number;
  10. }
  11. let add1:ISum = add;

可选参数/默认参数

  1. // 使用?表示可选参数,可选参数必须放后边位置
  2. const userName=(firstName:string, lastName?:string):string => {
  3. return firstName + lastName
  4. }
  5. // 默认参数,可以给参数设置一个默认值
  6. const defaultUserName=(firstName:string="shen", lastName?:string):string => {
  7. return firstName + lastName
  8. }

重载

同一个函数赋值不同个数的参数,或者参数的类型不同

  1. function setParams(parasA:string):string{
  2. return parasA.toUpperCase();
  3. }
  4. function setParams(parasA:string,paramsB:number):string{
  5. return parasA.repeat(paramsB)
  6. }

定义类class

  • 使用extends继承父类
  • 重写父类方法时,使用super可以调用父类中定义的方法 ```typescript class Language { name: string = “ts”; work() { return “writing “ + this.name; } } class Front extends Language { work() { return “前端开发” + super.work(); } } let l1 = new Language(); console.log(l1.work());

let l2 = new Front(); console.log(l2.work());

  1. <a name="96d5f3bf06f8b8fed47f3a1c2a8f6fb2"></a>
  2. ### 定义属性时,设置访问类型修饰符
  3. - private 私有
  4. - protected 子类可用
  5. - public 公用
  6. ```typescript
  7. class Person {
  8. constructor(public name:string, protected age: number, private password:string){
  9. this.name = name;
  10. this.age = age;
  11. this.password = password;
  12. }
  13. }

readonly只读,不可修改

  1. class Person {
  2. constructor(public readonly name:string){
  3. this.name = name;
  4. }
  5. }
  6. let sam = new Person("sam")
  7. //无法分配到 "name" ,因为它是只读属性
  8. sam.name = "xxx"

constructor构造器

  1. class Man {
  2. // 传统写法
  3. // public name:string;
  4. // constructor(name:string){
  5. // this.name = name;
  6. // }
  7. // 简化写法
  8. constructor(public name: string) {}
  9. }

构造器中参数的继承,super关键字

  1. class Man {
  2. constructor(public name: string) {}
  3. work(n:string){
  4. console.log(n)
  5. }
  6. }
  7. class Boy extends Man {
  8. constructor(public age: number) {
  9. // 可以继承父类的name属性
  10. super("ts");
  11. }
  12. getBoyInfo() {
  13. return `${this.name} + ${this.age}`;
  14. }
  15. work(n:string){
  16. console.log("boy work");
  17. // 可以用super关键字调用父类的方法
  18. super.work(n)
  19. }
  20. }
  21. let b = new Boy(1);

静态属性

getter & setter属性定义的为原型属性

  • private定义的私有属性,外部无法访问,可以使用getter和setter解决
  • 内部私有属性一般定义为 “_属性”
  • getter/setter定义时看似为函数,实际调用时是作为属性

    1. class Person {
    2. constructor(private _name: string) {}
    3. get name() {
    4. return this._name + "加密";
    5. }
    6. set name(name: string) {
    7. this._name = name;
    8. }
    9. }
    10. let p = new Person("ts");
    11. console.log(p.name);
    12. p.name = "js";
    13. console.log(p.name);

    static静态属性,类本身的属性

    属性是通过类直接调用,不用实例化调用

    1. class Instance {
    2. private static _instance: Instance;
    3. // private构造器,不能实例化类
    4. private constructor() {}
    5. static getInstance() {
    6. if (!this._instance) {
    7. this._instance = new Instance();
    8. }
    9. return this._instance;
    10. }
    11. }
    12. // 通过类Instance直接调用getInstance方法
    13. const demo1 = Instance.getInstance();
    14. const demo2 = Instance.getInstance();
    15. console.log(demo1 === demo2);

    抽象类abstract

  • 多个类要同时实现一个方法,可以把该方法定义为一个抽象类中的抽象方法

  • 抽象类只能被继承,不能被实例化
  • 抽象类中的抽象方法,不能写具体的实现
  • 继承了抽象类的类,必须实现抽象类中的抽象方法 ```typescript abstract class Gemo { getType() { return “Gemo type.”; } abstract getArea(): number; }

class Circle extends Gemo { constructor(public radius: number) { super(); } getArea() { return this.radius Math.PI; } } class Square extends Gemo { constructor(public width: number) { super(); } getArea() { return this.width this.width; } }

  1. <a name="b3b86024c7a3631f62a16a743ffd4938"></a>
  2. ## 泛型
  3. 泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型<br />根据业务最初的设计,函数identity入参为string
  4. ```javascript
  5. function identity(arg: String){
  6. return arg
  7. }
  8. console.log(identity('100'))

但是随着业务迭代过程,参数需要支持number

  1. function identity(arg: String){
  2. return arg
  3. }
  4. console.log(identity(100)) // Argument of type '100' is not assignable to parameter of type 'String'.

注意尽量避免使用any,any会丢失一些信息,我们无法确定返回值是什么类型。

泛型可以保证入参和返回值是相同类型,泛型是一种特殊的变量,只用于表示类型而不是值。

语法:(arg:T):T 其中T为自定义变量

  1. const hello : string = "Hello vue!"
  2. function say<T>(arg: T): T {
  3. return arg;
  4. }
  5. console.log(say(hello)) // Hello vue!

泛型约束,

使用interface和extends进行类型约束

泛型无法保证每种类型都有某一属性

  1. const hello : string = "Hello vue!"
  2. function say<T>(arg: T): T {
  3. console.log(arg.length) // Property 'length' does not exist on type 'T'.
  4. return arg;
  5. }
  6. console.log(say(hello)) // Hello vue!

要在约束层面上就提示错误,需要定义一个接口来描述约束条件

  1. interface Lengthwise {
  2. length: number;
  3. }
  4. function say<T extends Lengthwise>(arg: T): T {
  5. console.log(arg.length)
  6. return arg;
  7. }
  8. console.log(say(1)) // Argument of type '1' is not assignable to parameter of type 'Lengthwise'.
  9. console.log(say({value: 'hello vue!', length: 10})) // { value: 'hello vue!', length: 10 }

范型变量

范型T可作为变量,传入到args的类型定义中。

  1. function getLength<T>(args:T[]):T[]{
  2. console.log(args.length)
  3. return args;
  4. }

范型类型

  1. function identity<T>(args:T):T{
  2. return args
  3. }
  4. interface setIdentityFn<T>{
  5. (args:T) : T
  6. }
  7. let myIdentity: setIdentityFn<number> = identity

范型约束,约束对象的属性,

使用extends和对象的keyof

  1. function getProperty<T, U extends keyof T>(obj:T, key: U):T[U]{
  2. return obj[key]
  3. }
  4. let test = {a:1,b:2,c:3,d:4}
  5. console.log(getProperty(test,"a"))
  6. // test不包含e属性,调用就会报错
  7. // console.log(getProperty(test,"e"))

类使用范型

定义的类,也可以进行范型约束,约束内部变量data的类型。通过新建类时指定传入的类型。

  1. class Queue<T>{
  2. private data:T[] = []
  3. push(item:T){
  4. return this.data.push(item)
  5. }
  6. pop():T{
  7. return this.data.shift()
  8. }
  9. }
  10. const queue = new Queue<number>()
  11. queue.push(66)
  12. console.log(queue.pop().toFixed())

接口interface的范型约束

  1. interface KeyPair<T, U>{
  2. key : T
  3. value: U
  4. }
  5. let k1: KeyPair<string, number> = {key: "str", value:100}
  6. let k2: KeyPair<number, string> = {key: 66, value: "string"}

高级类型

联合类型

  1. interface JavaInterface {
  2. helloJava(): void;
  3. build(): void;
  4. }
  5. interface JsInterface {
  6. helloJavaScript(): void;
  7. build(): void;
  8. }
  9. class Java implements JavaInterface {
  10. static Strong:boolean = true;
  11. helloJava() {
  12. console.log("Hello Java")
  13. }
  14. build(){
  15. console.log("build Java")
  16. }
  17. }
  18. class JavaScript implements JsInterface {
  19. static Strong:boolean = false;
  20. helloJavaScript() {
  21. console.log("Hello JavaScript")
  22. }
  23. build(){
  24. console.log("build JavaScript")
  25. }
  26. }
  27. // ts类型保护
  28. enum Type { Strong, Week };
  29. function getLanguage(type: Type):JavaInterface|JsInterface {
  30. let lang = type === Type.Strong ? new Java() : new JavaScript();
  31. return lang
  32. }
  33. let j = getLanguage(Type.Strong)
  34. // 只可以调用联合类型
  35. console.log(j.build());
  36. // 下面两个调用都会报错
  37. // console.log(j.helloJava(),j.helloJavaScript());

交叉类型

  1. // 交叉类型 a&b
  2. function combine<T, U>(objA:T, objB:U):T & U{
  3. let result = {} as T & U;
  4. for(let id in objA){
  5. result[id] = objA[id] as any
  6. }
  7. for(let id in objB){
  8. if(!result.hasOwnProperty(id)){
  9. result[id] = objB[id] as any
  10. }
  11. }
  12. return result;
  13. }
  14. class Person{
  15. constructor(public name:string){
  16. this.name = name;
  17. }
  18. }
  19. interface logger{
  20. log(): void;
  21. }
  22. class ConsoleLog implements logger{
  23. log(){
  24. console.log("class ConsoleLog ");
  25. }
  26. }
  27. let sam = combine(new Person("sam"), new ConsoleLog())
  28. console.log(sam.name, sam.log());

类型保护

1.类型断言as
通过as断言,判断对象的类型

  1. interface Bird{
  2. fly(height: number): void;
  3. layEggs(num: number): string;
  4. }
  5. interface Fish{
  6. swim(): boolean;
  7. layEggs(num: number):string;
  8. }
  9. class BirdC implements Bird {
  10. fly(height: number): void{
  11. console.log(`fly ${height}m`);
  12. }
  13. layEggs(num: number):string{
  14. return `lay ${num} eggs`
  15. }
  16. }
  17. class FishC implements Fish {
  18. swim(): boolean{
  19. console.log("can swim");
  20. return true;
  21. }
  22. layEggs(num: number):string{
  23. return `lay ${num} eggs`
  24. }
  25. }
  26. type Pet = FishC | BirdC;
  27. function getPet(p:Pet):Pet{
  28. return p
  29. }
  30. let pet = getPet(new BirdC())
  31. let petFish = getPet(new FishC())
  32. // 这里只能使用共用的属性
  33. console.log(pet.layEggs(6));
  34. // 要想使用不同的属性,可以使用as断言
  35. if((pet as FishC).swim){
  36. (pet as FishC).swim()
  37. }
  38. if((pet as BirdC).fly){
  39. (pet as BirdC).fly(10)
  40. }
  41. if((petFish as FishC).swim){
  42. (petFish as FishC).swim()
  43. }

2.类型谓词 instance is classA

  1. type Pet = FishC | BirdC;
  2. // 可以使用实例is类,判断对象的类型,进行类型保护,然后就可以调用不同类型的方法
  3. function isFish(pet: Fish | Bird): pet is Fish{
  4. return (pet as Fish).swim !== undefined;
  5. }
  6. function getPet(p:Pet):Pet{
  7. if(isFish(p)){
  8. p.swim();
  9. } else {
  10. p.fly(8);
  11. }
  12. return p
  13. }
  14. let pet = getPet(new BirdC())
  15. let petFish = getPet(new FishC())

3:typeof判断基本类型

  1. function getLanguage( x: string | number = "default") {
  2. if(typeof x ==="string"){
  3. x = x.toUpperCase()
  4. }else{
  5. x = x.toFixed(2)
  6. }
  7. return { x }
  8. }

4:instanceof判断对象

  1. type Pet = FishC | BirdC;
  2. function getPet(p:Pet):Pet{
  3. if(p instanceof FishC){
  4. p.swim();
  5. } else {
  6. p.fly(8);
  7. }
  8. return p
  9. }

5:in关键字

  1. type Pet = FishC | BirdC;
  2. function getPet(p:Pet):Pet{
  3. if("swim" in p){
  4. p.swim();
  5. } else {
  6. p.fly(8);
  7. }
  8. return p
  9. }

extends keyof和in keyof的区别

  1. type Partial<T> = {
  2. [P in keyof T]?: T[P];
  3. };
  1. type Pick<T, K extends keyof T> = {
  2. [P in K]: T[P];
  3. };