TypeScript 基础手册

134 阅读7分钟

TypeScript 基础知识

快速入门

创建项目文件夹 与 ts 文件夹,在ts文件夹里面添加 ts为后缀的文件 test1.ts 在里面输入一个js 输入代码

输入下列指令安装 typescript

npm i -g typescript

在ts文件夹下进行 tsc xx.ts

如果报错使用下面指令,逐条输入

PS E:\typscriptproject> get-ExecutionPolicy
PS E:\typscriptproject> set-ExecutionPolicy RemoteSigned
PS E:\typscriptproject> get-ExecutionPolicy

变量类型的声明

在test1 里面输入 代码 声明变量的类型这样就可以将js 代码里面的变量类型锁定什么的变量

let a : number;

如果前面直接变量加赋值之后就可以进行自动声明类型

let d =123;

可以在ts 里面写函数


var a=11;
var b=22;

var sum=function (a:number,b:number): number{
 return a+b;
}

console.log(sum(a,b));

typescript 中类型的声明

布尔值类型 在typescript 中 可以声明 布尔值类型 boolean

let isDone: boolean=false;

数值类型 number

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;

使用空值 void表示返回的函数值

function alertName(): void {
    alert('My name is Tom');
}

any 类型就是任意类型(尽量不用) ,如果实在不自动什么类型使用 unknown 类型

null 与 undefined

void 的区别是,undefinednull 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:

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

联合类型

就是变量可以拥有多个类型

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

形参也允许拥有多个类型

function getString(something: string | number): string {
    return something.toString();
}

数组类型

命名规则 可以用类型 加[]

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

数组泛型

let f:Array<number>=[1,2,3,4];
这里定义了数组类型的数组

用接口定义数组的结构

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
// 上面的泛型规定只有下标是数字,里面的元素也是方向

function sum() {
    let args: {
        [index: number]: number;
        length: number;
        callee: Function;
    } = arguments;
}

函数声明

传统的函数声明一般是有声明式和表达式两种

// 函数声明(Function Declaration)
function sum(x, y) {
    return x + y;
}

// 函数表达式(Function Expression)
let mySum = function (x, y) {
    return x + y;
};

在ts 里面可以定义为下面两种

function sum(x: number, y: number): number {
    return x + y;
}
sum(1, 2, 3);
let mySum = function (x: number, y: number): number {
    return x + y;
};

使用接口形式定义函数

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

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

定义可选参数就是在形参名后面加一个?可选参数放后面

let mysearch :SearchFunc;
mysearch=function(source:string,substring?:string){
if(substring){
    if(source==substring){
        return true;
     }
     else{
        return false;
     }
}
else{
    return false;
}
  
}

console.log(mysearch('s'));

剩余函数参数

function arrPush2(arry,...items){
    items.forEach(item => arry.push(item))
    return arry;
}

let arr2:number[]=[1,2,4];

console.log(arrPush2(arr2,...[3,3,4]));

重载

上面两个是定义类型

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

类型断言

人为规定一个值的类型

一般用上面的那种比较合适

as 类型
<类型>值

使用父子断言的形式可以实现多态的效果,在定义形参的时候定义父接口,实参调用的时候使用

interface ApiError extends Error {
    code: number;
}
interface HttpError extends Error {
    statusCode: number;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    return false;
}
  • 联合类型(形参有多个类型的情况)可以被断言为其中一个类型
  • 父类可以被断言为子类
  • 任何类型都可以被断言为 any
  • any 可以被断言为任何类型
interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

let tom: Cat = {
    name: 'Tom',
    run: () => { console.log('run') }
};
let animal: Animal = tom;

左侧定义的类型大于右侧的类(多态)

双重断言

由于any类型可以被任何类型断言,也可以用于任何类型的断言

interface Cat {
    run(): void;
}
interface Fish {
    swim(): void;
}

function testCat(cat: Cat) {
    return (cat as any as Fish);
}

断言只是在编译阶段修改变量的类型,不是类型转化

断言有点像多态在函数输出类型不可知的情况下直接返回 any 类型

再用的时候进行断言

因此不同赋值的情况下应使用 左边类型大于右边的

interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

const animal: Animal = {
    name: 'tom'
};
let tom = animal as Cat; //左边类型是any

使用泛型来实现灵活的类型

声明语法

declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
export 导出变量
export namespace 导出(含有子属性的)对象
export default ES6 默认导出
export = commonjs 导出模块
export as namespace UMD 库声明全局变量
declare global 扩展全局变量
declare module 扩展模块
/// <reference /> 三斜线指令

类型别名type

可以将一个混合类型重新命名

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}
type EventNames = 'click' | 'scroll' | 'mousemove'; //字面量类型,可以在多个字符串里面选一个

元组的声明

数组是同类型的组合,元组是不同的类型的组合

元组直接在里面声明了多种类型:

let tom: [string, number] = ['Tom', 25];

可以像数组一样进行操作

枚举类型是对一些常用变量进行匹配

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
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

每个值会自动对应一个从零开始的下标也可自定义参数的值

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

未被定义值的变量会承接上一个进行赋值

存在赋值覆盖的问题

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

上面的例子中,递增到 3 的时候与前面的 Sun 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 "Sun",而后又被 "Wed" 覆盖了。编译的结果是:

带计算式 的赋值要放在最后面(如果还有没赋值的情况)

es6 回顾

类和java很想不过属性的的定义要赋值属性

class Animal {
    public name;
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is ${this.name}`;
    }
}

let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack

继承

class Cat extends Animal

get set 方法

class Animal {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return 'Jack';
  }
  set name(value) {
    console.log('setter: ' + value);
  }
}

let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

static 不需实例化之间调用类名调用

变量的权限

public private 和 protected readonly

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

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom

readonly 使用范围

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。

只读属性关键字 readonly和其他权限一起使用时,放后面

抽象类

抽象类时用于定义类的外形里面没有实际功能,子类继承抽象类型,对里面的方法进行重写 使用 abstract 进行命名

接口

接口预定义好结构的抽象类,其他类只需要调用就可以了

interface Alart{
    types:string;
    bark():void;
}

class Car implements Alart{
     types: string;
     constructor(types){
      this.types=types;
     }
     bark(){
        console.log("警告"+`有人${this.types}`);
        
     }
}

let bens= new Car('hh');
bens.bark();

接口继承

接口继承接口


interface A1 {
    name:string;
}

interface A2 extends A1{
    id:string;
}
class Cars implements A2{
   name:'bens';
   id='001';
}

let benss= new Cars();
console.log(benss.id);

常见的面向对象语言中,接口是不能继承类的,但是在 TypeScript 中却是可以的

接口继承类

本质上接口继承接口与接口继承类是一样的

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

泛型

泛型就是在定义的时候不对类型进行定义,在使用的时候再定义

大致效果与下面类似

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

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

console.log(createArray<string>(3,'x'));

多个类型用逗号隔开

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

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

由于泛型可以自由定义,可定义接口来约束泛型

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
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']

如上就是泛型接口

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

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; };

同时泛型是可以可以进行预定义的类型的

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;
}

接口合并(相当于类的重载)

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

只需要接口名字一样就可以,就会进行重载合并