美文网首页typeScript笔记
typescript学习整理

typescript学习整理

作者: 大喵爱读书 | 来源:发表于2019-05-12 00:05 被阅读0次

typescript现在已经慢慢变成前端工程师必学必会的技能了,它在开发大型应用的时候可以让产品更加可控,本篇就试着理出typescript一些必须要掌握的知识点。

原始类型

js有五种基本类型 string、number、boolean、null、undefined,这几种类型typescript都赋予了对应的类型限定,如下:

  1. 布尔值

      let bool: boolean = false
    

    注意利用Boolean创造的对象不是布尔值:

typescript现在已经慢慢变成前端工程师必学必会的技能了,它在开发大型应用的时候可以让产品更加可控,本篇就试着理出typescript一些必须要掌握的知识点。

原始类型

js有五种基本类型 string、number、boolean、null、undefined,这几种类型typescript都赋予了对应的类型限定,如下:

  1. 布尔值

       bool: boolean = false     
    

    注意`利用Boolean创造的对象不是布尔值:

    let boolObject: boolean = new Boolean(1) //会报错
    

    new Boolean创造的是一个对象并不是布尔值,

    直接调用Boolean倒是可以

     let bool: boolean = Boolean(1) 
    
  2. 字符串

     str: string = 'hellow'
    // 模板字符串
    let sentence: string = `Hello, my name is ${myName}.
    I'll be ${myAge + 1} years old next month.`;      
    

    编译结果:

      var myName = 'Tom';
    var myAge = 25;
    // 模板字符串
    var sentence = "Hello, my name is " + myName + ".\nI'll be " + (myAge + 1) + " years old next month.";      ```
    
  3. 数值

    let decLiteral: number = 6;
    let hexLiteral: number = 0xf00d;
    // ES6 中的二进制表示法
    let binaryLiteral: number = 0b1010;
    // ES6 中的八进制表示法
    let octalLiteral: number = 0o744;
    let notANumber: number = NaN;
    let infinityNumber: number = Infinity;     
    

    编译结果:

       var decLiteral = 6;
    var hexLiteral = 0xf00d;
    // ES6 中的二进制表示法
    var binaryLiteral = 10;
    // ES6 中的八进制表示法
    var octalLiteral = 484;
    var notANumber = NaN;
    var infinityNumber = Infinity;      
    

    其中 0b10100o744ES6 中的二进制和八进制表示法,它们会被编译为十进制数字。

  4. null和undefined

    null和undefined在typescript中分别是两个独立的类型限定:

      let u: undefined = undefined;
      let n: null = null;     
    

    undefined限定的变量只能赋值undefined,null也一样。

  5. 空值void
    void限定的变量只能赋值null或者undefined:

     let noContent: void = null;
    

    与null和undefined限定不同的是,null和undefined是所有类型的子类型:

      let num: number = undefined; //这样没有问题
    // 这样也不会报错
    let u: undefined;
    let num: number = u;
    

    而void类型的变量不能互相随意传递:

      let noContent: void;
      let num: number = noContent; // 报错
    
  6. 任意类型 (any)

    被指定为任意类型的变量可以被赋值任何类型,也能被调用任何属性方法,都不会报错:

    let myFavoriteNumber: string = 'seven';
    myFavoriteNumber = 7;
    
    let anyThing: any = 'Tom';
    anyThing.setName('Jerry');
    anyThing.setName('Jerry').sayHello();
    anyThing.myName.setFirstName('Cat');
    

    注意:没有指定类型的变量,默认就是any类型

类型推论

当变量被赋值的时候没有被指定类型限定,会根据变量类型默认设置类型限定:

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

// 等价于下边
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;

联合类型

表示取值为多种类型中的一个

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// 只能赋值string或者number
myFavoriteNumber = true // 会报错

访问联合类型的属性和方法

联合类型的变量只能访问类型共有的属性和方法

function getLength(something: string | number): number {
   return something.length; // 会报错,字符串不含有length属性
 //想解决报错可以使用类型断言
 return <string>something.length // 这样就不会报错
}
// 联合类型当赋值为一个类型会推断赋值为那个类型
myFavoriteNumber = 'ss' // 变量被设定为字符串
myFavoriteNumber.slice(0) // ok
myFavoriteNumber = 5 // 变量被推断为number类型
myFavoriteNumber.slice(0) // 会报错

接口

接口是面向对象中的概念,表示行为的抽象,具体如何行动通过类实现,在typescript中本人觉得接口主要是起到对编码中的一种约束,可以减少很多错误,并且配合很多ide(比如webstorm)代码提示可以提高编码效率。

简单例子

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
}; // ok
let lilei: Person = {
      name: 'Tom',
    age: 25,
  sexy: 'name'
} // 错误 sexy属性并没有定义

可选属性

上边例子中定义的Person接口限定的变量,必须同时设置name和age两个属性,不然会报错:

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
}; // error 缺少 age属性

如果要解决这个,就需要设置可选属性,在变量前边加上

interface Person {
   name: string;
   age?: number;
}

let tom: Person = {
   name: 'Tom'
};//ok

任意属性

有时候我们希望一个接口准许有任意属性,可以使用如下方式:

interface Person {
   name: string;
   age?: number;
   [propName: string]: any;
}

let tom: Person = {
   name: 'Tom',
   gender: 'male'
}; // ok

使用 [propName: string] 定义了任意属性取 string 类型的值。

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

interface Person {
  name: string;
  age?: number;
  [propName: string]: string;
}

let tom: Person = {
  name: 'Tom',
  age: 25,
  gender: 'male'
};

上边例子中[propName: string]: string;将整个接口设置;属性名为string类型,属性值为string类型,age值为number不符合这个类型,所以报错。

只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性,只读属性有两个约束:

  1. 对象创建时候要赋值

  2. 赋值后不能再重新修改属性值了

例子:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
}; // 报错 没有给id赋值

tom.id = 89757; // id是只读,不能修改

数组类型

数组类型定义方式

类型 + 方括号

let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push(true) // 报错  不是number类型

数组范型

let fibonacci: Array<number> = [1, 1, 2, 3, 5];

接口表示

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

函数类型

函数声明

function sum(x: number, y: number): number {
    return x + y;
}

// 表达式声明方式
let mySum = function (x: number, y: number): number {
    return x + y;
};

Typescript添加了对函数变量和返回值类型的限定

接口定义函数类型

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

可选参数

函数有时候会定义一些是必须传递的参数,函数的可选参数定义和接口中对象的可选参数定义方式一样,都是使用,如下:

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom')

注意:可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必须参数了

function buildName(firstName?: string, lastName: string) {
    if (firstName) {
        return firstName + ' ' + lastName;
    } else {
        return lastName;
    }
} // 报错

参数默认值

在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

此时就不受可选参数必须接在必须参数后面的限制了:

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');

剩余参数

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数):

function push(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。

方式

<类型>值

值 as 类型

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。

使用场景

之前有个例子当函数变量使用联合类型,如果我们在函数体中需要特定类型的方法,就需要使用类型断言,不然编译的时候就会报错:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length; //不加类型断言就会报错
    } else {
        return something.toString().length;
    }
}

注意,类型断言不是类型转换,断言一个联合类型不存在的类型会报错:

function toBoolean(something: string | number): boolean {
    return <boolean>something; // 报错, 布尔类型不能赋值给something
}

字符串字面量类型

字符串字面量类型用来约束取值只能是某几个字符串中的一个

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'

元组

元组可以理解为存储不同类型值的数组

let tulpe: [string, number] = ['tom', 25];
//或者
let tulpe: [string, number];
tulpe[0] = 'tom';
tulpe[1] = 25;

tulpe[0].slice(1);
tulpe[1].toFixed(2);

//也可以只赋值其中一项
let tulpe: [string, number];
tulpe[0] = 'tom';

tulpe.push(true);// 报错 赋值类型不存在申明类型中

元组使用上需要特别注意的是:

元组初始化的时候必须赋值元组声明的所有类型变量:

let tulpe: [string, number] = ['tom']; // 报错
// 或者
let tulpe: [string,number];
tulpe = ['tome'] // 报错

枚举

枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。

典型例子

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

//编译后的样子
var Days;
(function (Days) {
   Days[Days["Sun"] = 0] = "Sun";
   Days[Days["Mon"] = 1] = "Mon";
   Days[Days["Tue"] = 2] = "Tue";
   Days[Days["Wed"] = 3] = "Wed";
   Days[Days["Thu"] = 4] = "Thu";
   Days[Days["Fri"] = 5] = "Fri";
   Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

手动复制

我们也可以给枚举项手动赋值:

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

未手动赋值的枚举类型变量会自动赋值(在前边变量值逐步累加1)

值得注意的是:如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true

常数项和计算所得项

枚举项有两种类型:常数项(constant member)和计算所得项(computed member)。

前面我们所举的例子都是常数项,一个典型的计算所得项的例子:

enum Color {Red, Green, Blue = "blue".length};

上面的例子中,"blue".length 就是一个计算所得项。

上面的例子不会报错,但是如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错

enum Color {Red = "red".length, Green, Blue};

常数枚举

常数枚举是使用 const enum 定义的枚举类型:

const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员

上例的编译结果是:

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

外部枚举

外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型:

declare enum Directions {
   Up,
   Down,
   Left,
   Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

之前提到过,declare 定义的类型只会用于编译时的检查,编译结果中会被删除。

上例的编译结果是:

var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

typescript中类增加的内容

  1. 限定符

    TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 publicprivateprotected

    • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public

    • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问

    • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

  2. 抽象类

     abstract class Animal { // abstract标志类是抽象类
        public name;
        public constructor(name) {
            this.name = name;
        }
        public abstract sayHi();
        }
      let a = new Animal('Jack'); // 报错 抽象类不能实例化

抽象类正确的使用方式:

       abstract class Animal {
        public name;
        public constructor(name) {
              this.name = name;
        }
        public abstract sayHi();
        }

      class Cat extends Animal {
        public sayHi() { // 实现抽象类中的方法
          console.log(`Meow, My name is ${this.name}`);
        }
    }
    let cat = new Cat('Tom');

类的类型

类也可以作为一个类型来使用

    class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    sayHi(): string {
      return `My name is ${this.name}`;
    }
    }
    let a: Animal = new Animal('Jack');
    console.log(a.sayHi()); // My name is Jack

类与接口

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。

类实现接口

interface Alarm {
   alert();
}

class Door {
}

class SecurityDoor extends Door implements Alarm {
   alert() {
       console.log('SecurityDoor alert');
   }
}

class Car implements Alarm {
   alert() {
       console.log('Car alert');
   }
}

接口继承接口

interface Alarm {
   alert();
}

interface LightableAlarm extends Alarm {
   lightOn();
   lightOff();
}

接口继承类

接口也可以继承类:

class Point {
   x: number;
   y: number;
}

interface Point3d extends Point {
   z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

典型的例子

首先,我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值:

function createArray<T>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
      result[i] = value;
  }
  return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']

上例中,我们在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。

接着在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来:

多个类型参数

定义泛型的时候,可以一次定义多个类型参数:


function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]

泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:


function loggingIdentity<T>(arg: T): T {
  console.log(arg.length);
  return arg;
}

上例中,泛型 T 不一定包含属性 length,所以编译的时候报错了。

这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束:

nterface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

多个类型参数之间也可以互相约束:


function copyFields<T extends U, U>(target: T, source: U): T {
  for (let id in source) {
      target[id] = (<T>source)[id]; // 如果不使用泛型约束,这里会报错
  }
  return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };

copyFields(x, { b: 10, d: 20 });

上例中,我们使用了两个类型参数,其中要求 T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。

泛型接口

interface CreateArrayFunc {
  <T>(length: number, value: T): Array<T>;
}

let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
      result[i] = value;
  }
  return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

进一步,我们可以把泛型参数提前到接口名上:


interface CreateArrayFunc<T> {
  (length: number, value: T): Array<T>;
}

let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
      result[i] = value;
  }
  return result;
}

createArray(3, 'x'); // ['x', 'x', 'x'] 

注意,此时在使用泛型接口的时候,需要定义泛型的类型。

泛型类

与泛型接口类似,泛型也可以用于类的类型定义中:

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型参数的默认类型

在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

function createArray<T = string>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
      result[i] = value;
  }
  return result;
}

声明合并

如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型:

函数的合并

我们可以使用重载定义多个函数类型:

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
  if (typeof x === 'number') {
      return Number(x.toString().split('').reverse().join(''));
  } else if (typeof x === 'string') {
      return x.split('').reverse().join('');
  }
}

接口的合并

接口中的属性在合并时会简单的合并到一个接口中:

interface Alarm {
    price: number;
}
interface Alarm {
    weight: number;
}

相当于:

interface Alarm {
  price: number;
  weight: number;
}

注意,合并的属性的类型必须是唯一的

interface Alarm {
  price: number;
}
interface Alarm {
  price: number;  // 虽然重复了,但是类型都是 `number`,所以不会报错
  weight: number;
}
interface Alarm {
  price: number;
}
interface Alarm {
  price: string;  // 类型不一致,会报错
  weight: number;
}

// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type.  Variable 'price' must be of type 'number', but here has type 'string'.

接口中方法的合并,与函数的合并一样:

interface Alarm {
    price: number;
    alert(s: string): string;
}
interface Alarm {
    weight: number;
    alert(s: string, n: number): string;
}

相当于:

interface Alarm {
  price: number;
  weight: number;
  alert(s: string): string;
  alert(s: string, n: number): string;
}

参考资料

相关文章

  • typescript学习整理

    typescript现在已经慢慢变成前端工程师必学必会的技能了,它在开发大型应用的时候可以让产品更加可控,本篇就试...

  • Typescript

    TypeScript(TS)部分 TypeScript学习笔记

  • Vue-cli 整合 Typescript 筆記

    整理自:三命:Vue + TypeScript 新项目起手式最新版:Vue-cli 整合 Typescript 筆...

  • typescript学习

    typescript学习

  • TypeScript入门教程(一)

    学习网址:文档简介 · TypeScript中文网 一、Typescript介绍 1. TypeScript 是由...

  • typescript

    title: typescript学习tags: typescript学习 [toc] 泛型 基本使用 两种使用方...

  • TypeScript 基础

    以下为学习极客时间 《TypeScript 开发实战》的学习记录。 TypeScript 环境配置 安装 ts: ...

  • Typescript 学习笔记六:接口

    目录: Typescript 学习笔记一:介绍、安装、编译 Typescript 学习笔记二:数据类型 Types...

  • TypeScriptz学习笔记

    TypeScriptz学习笔记 标签(空格分隔): TypeScript 撩课学院 安装TypeScript Ty...

  • Typescript

    学习笔记 菜鸟教程 《菜鸟教程》-TypeScript简介 《菜鸟教程》-TypeScript安装 《菜鸟教程》-...

网友评论

    本文标题:typescript学习整理

    本文链接:https://www.haomeiwen.com/subject/ttlfaqtx.html