TypeScript:使用笔记

**注意:**本文章不能作为新手入门教程使用,仅做为查阅

参考文章来源:

  • https://blog.csdn.net/u012031958/article/details/106922480/
  • https://www.cnblogs.com/lemonyam/p/11215603.html
  • 《TypeScript中文指南》
  • https://ts.xcatliu.com/introduction/get-typescript.html
一、概念
1.什么是Typscript
  • TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。

  • 简单的说,它就是针对弱类型语言JavaScript的一种强类型约束

2.特点或优势
  • TypeScript 增加了代码的可读性和可维护性,可以在编译阶段就发现大部分错误,这总比在运行时候出错好
  • 强大的兼容性:TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可,设计目标是开发大型应用,它可编译成纯 JavaScript,编译出来的 JavaScript 可运行在任何浏览器上
  • 支持 ES6 标准,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改
二、入门使用
1.安装Typescript

以下命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。

npm install -g typescript

编译一个 TypeScript 文件很简单,我们约定使用 TypeScript 编写的文件以 .ts 为后缀

tsc hello.ts
2.第一个ts文件体验

新建一个hello.ts,输入以下代码:

function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = 'Tom';
console.log(sayHello(user));

使用命令tsc hello.ts编译后产生js文件,代码如下

function sayHello(person) {
    return 'Hello, ' + person;
}
var user = 'Tom';
console.log(sayHello(user));

在 TypeScript 中,我们使用 : 指定变量的类型,: 的前后有没有空格都可以。

上述例子中,我们用 : 指定 person 参数类型为 string。但是编译为 js 之后,并没有什么检查的代码被插入进来。

这是因为 TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。

三、数据类型

Typescript不仅支持Javascript原有的数据类型,而且还新增了几个数据类型,不过当然仅限于在ts中,编译后还是js的那几个类型

1.常见的数据类型
数据类型 说明
boolean 布尔值
number 数值类型
string 字符串类型
Array 数组类型
tuple 元组类型(属于数组的一种,即数组中每一个元素指定类型)
enum 枚举类型
any 任意类型,变量赋值不受类型限制(注意:尽量少用这个类型)
undefined 未定义类型
null 空值
void 多作用方法上,表示无返回值
never 代表从来不会出现的值,也就是指上面数据类型之外的类型
2.使用示例
// 1.定义一个boolean类型,显式声明值,不然会出警告
let flag:boolean = true;
console.log(flag);


// 2.定义一个number类型,只能接受数字类型
let num:number = 123;
let floatNum:number = 12.3;
console.log(num,floatNum);


// 3.定义一个字符串类型
let str:string = "this is a string";
console.log(str);


// 4.定义一个数组类型
// 表示数组元素只能包含数值类型
let arr:number[] = [1,2,3,14.5];
// 定义方式写法2,使用泛型定义
let arr2:Array<string> = ['a','b','c'];
console.log(arr,arr2);


// 5.定义一个元组类型,可以为每一个元素指定具体类型
let tupleArr:[boolean,number,string] = [false,123,'abc'];
console.log(tupleArr);


// 6.定义一个枚举类型,声明枚举类型使得常量更加安全,更具有语义化
// 支付状态
enum PayState{
    success = 1, /* 支付成功 */
    error = 2, /* 支付失败 */
    nopay = 3 /* 未支付 */
}
console.log(PayState.success);
// 未指定值时,默认值就是元素下标
enum Color {Red,Green,Blue}
let a:Color = Color.Red;
console.log(a,Color.Green,Color.Blue);


// 7.定义一个any类型,变量不受限制
let anyVariable:any = 123;
anyVariable = "anyVariable";
console.log(anyVariable);

// 8.定义一个无具体值的类型,这类类型是联合类型
// 一个元素可能是number类型,可能是null或者undefined
let variable:number | null | undefined;
console.log(variable);


// 9.void定义方法返回值
// 无返回值
function noReturnValue():void {
    console.log('running...')
}
// 有返回值
function returnValue():number {
    return 1;
}
console.log(noReturnValue());
console.log(returnValue());


// 10.never从不会出现的数据类型
let neverValue:never;
四、函数的声明
1.js中的函数声明

在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression):

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

// 函数表达式(Function Expression),匿名函数
let mySum = function (x, y) {
    return x + y;
};
2.在ts中声明函数
2.1有返回值函数,函数必须return一个值
function isNumber():number {
    return 123;
}
2.2无返回值函数
function noValue():void {
    console.log('无返回值函数');
}
2.3匿名函数
let func = function ():number {
    return 111;
};
console.log('TS匿名函数返回值',func());
3.函数的参数
3.1必传参数
function hasParam(name:string,age:number) {
    console.log(`${name} --- ${age}`)
}
hasParam('zhangsan',20);
let hasParamAnon = function (name:string,age:number):string {
    return name+age;
};
console.log(hasParamAnon('xiaoming',18));
3.2可选参数
function customerParamFunc(name:string,age?:number) {
    if(age){
        console.log('姓名:',name,'年龄:',age);
    }else {
        console.log('姓名:',name,'年龄保密');
    }
}
customerParamFunc('xiaoming');
3.3带默认参数的函数,没传值就赋默认值
function defaultValue(name:string,age:number=18) {
    console.log(name,'最低年龄:',age);
}
defaultValue('xiaohong');
3.4展开运算符
function addNumber(...num:number[]):number {
    let sum:number = 0;
    for(let i=0;i<num.length;i++){
        sum = sum + num[i];
    }
    return sum;
}
console.log('相加结果为:',addNumber(1,2,3,4));
3.5函数重载
// ES5中不支持函数重载,同名函数下面会覆盖上面的
// ts可以声明同名函数
function getInfo(name:string):string;
function getInfo(age:number):string;
function getInfo(str:any):any{
    if(typeof str ==="string"){
        return "我叫:"+ str;
    }else{
        return "我的年龄是:" + str;
    }
}
五、类
1.类的概念

虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象(OOP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 CatDog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
  • 存取器(getter & setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
2.定义一个类

TS声明一个类

class Person {

    // 定义字段属性
    name:string;
    age:number;
    sex:string;

    // 构造方法
    constructor(name:string,age:number,sex:string) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    // 普通方法
    run():void{
        console.log('普通方法','is running...')
    }

    // setter,getter方法
    set setName(name:string){
        this.name =name;
    }

    get getName():string{
        return this.name;
    }

    set setSex(sex:string){
        this.sex = sex;
    }

    get getSex():string{
        return this.sex;
    }
}
let p1 = new Person('小明',20,'');
p1.setName = '小刚';
p1.setSex = '男';
console.log(p1);
3.类的继承

使用extends关键字即可

class Student extends Person{

    constructor(name:string,age:number,sex:string) {
        super(name,age,sex);
    }

    // 方法覆盖
    run(): void {
        // super.run();
        console.log('子类方法','is running...')
    }
}

let stu1 = new Student('小李',16,'男');
stu1.run();
4.抽象类

不能被实例化,子类继承后必须实现抽象类中的方法

abstract class Animal {
    public name:string = '';

    protected constructor(name:string) {
        this.name = name;
    }

    abstract eat():any;

}

class Dog extends Animal{

    constructor(name:string) {
        super(name);
    }

    // 普通方法
    run(){
        console.log(this.name,'dog is running');
    }

    // 必须实现抽象类中的方法
    eat(): any {
        console.log(this.name,'dog is eating');
    }
}
let dog = new Dog('中华田园犬');
dog.run();
dog.eat();
六、类与接口

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

举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它

1.类与接口的声明和使用
interface Alarm {
    alert(): void;
}

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(): void;
}

interface Light {
    lightOn(): void;
    lightOff(): void;
}

class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}
2.接口之间的继承

接口与接口之间可以是继承关系,这很好理解,LightableAlarm 继承了 Alarm,除了拥有 alert 方法之外,还拥有两个新方法 lightOnlightOff

interface Alarm {
    alert(): void;
}

interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
七、泛型

【参考:https://ts.xcatliu.com/advanced/generics.html】

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

1.简单的例子

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

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']

上例中,我们使用了之前提到过的数组泛型来定义返回值的类型。

这段代码编译不会报错,但是一个显而易见的缺陷是,它并没有准确的定义返回值的类型:

Array 允许数组的每一项都为任意类型。但是我们预期的是,数组中每一项都应该是输入的 value 的类型。

这时候,泛型就派上用场了:

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 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array 中即可使用了。

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

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(3, 'x'); // ['x', 'x', 'x']
2.泛型的多个参数

定义泛型的时候,可以一次定义多个类型参数,本例中,我们定义了一个 swap 函数,用来交换输入的元组。

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

swap([7, 'seven']); // ['seven', 7]
3.泛型约束

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

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

// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.

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

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

interface Lengthwise {
    length: number;
}

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

上例中,我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。

此时如果调用 loggingIdentity 的时候,传入的 arg 不包含 length,那么在编译阶段就会报错了:

interface Lengthwise {
    length: number;
}

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

loggingIdentity(7);

// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.

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

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 中不存在的字段。

4.泛型接口

使用泛型约束接口传入的类型,之前学习过,可以使用接口的方式来定义一个函数需要符合的形状:

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

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

当然也可以使用含有泛型的接口来定义函数的形状:

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']

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

5.泛型类

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

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; };
八、声明文件

【参考:https://www.cnblogs.com/chenwenhao/p/12392538.html】

1.为什么需要声明
  • 告诉编译器,某个项(对象,变量,包,类等等)已经存在了,不用检查。上面已经说过了,不再复述。

  • 对于一个你要发布的包,IDE在获得声明文件后,可以为程序员做代码提示。

例如,VS Code可以拿来做语法提示。如果你声明了一个对象A,A里面有b方法,c方法,分别是哪些参数等等。程序员引用包后,如果这个包里有声明文件被VS Code找到的话,(package.json里的types字段说明一下)程序员在调用这个A对象里,会有代码提示列表,b,c方法就列出来了。

当然,因为声明文件说明了你的包的抽象细节,各方面都可以拿来用

声明应该是纯粹对于一个标识符类型或外观的描述,便于编译器识别,外部声明具有以下特点:

  • 必须使用 declare 修饰外部声明
  • 不能包含实现或初始化信息(内部声明可以在声明的时候包含实现或初始化)
2.声明的使用例子
// 声明a为一个数字
declare let a: number;
// 错误,外部声明不能初始化
// error TS1039: Initializers are not allowed in ambient contexts
declare let b: number = 2;

// 声明T为一个接口
declare interface T {}
// 声明接口类型变量b
let b: T;

// 声明fn为一个函数
// 错误,声明包含了函数实现
// error TS1183: An implementation cannot be declared in ambient contexts
declare function fn(){}

// 正确,不包含函数体实现
declare function fn(): void;

// 声明myFunc为一个函数
declare let myFunc: (a: number) => void;

// 声明MyEnum枚举类型
declare enum MyEnum {
  A, B
}

// 声明NS为命名空间
declare namespace NS {
  // 错误,声明不能初始化
  // error TS1039: Initializers are not allowed in ambient contexts
  const a: number = 1;
  // 正确,仅包含声明
  const b: number;
  // 正确,函数未包含函数体实现
  function c(): void;
}

// 声明一个类
declare class Greeter {
    constructor(greeting: string);
    greeting: string;
    showGreeting(): void;
}

外部声明还可以用于声明一个模块,如果一个外部模块的成员要被外部访问,模块成员应该用 export 声明导出:

declare module 'io' {
  export function read(file: string): string;
  export function write(file: string, data: string): void;
}
3.全局变量的声明文件语法
  • declare var声明全局变量
  • declare function 声明全局方法
  • declare class声明全局类
  • declare enum声明全局枚举类型
  • declare namespace 声明(含有子属性的)全局对象
  • interfacetype 声明全局类型
3.1 declare var

在所有的声明语句中,declare var 是最简单的,如之前所学,它能够用来定义一个全局变量的类型。与其类似的,还有 declare letdeclare const,使用 let 与使用 var 没有什么区别

// src/jQuery.d.ts
declare let jQuery: (selector: string) => any;
// src/index.ts
jQuery('#foo');
// 使用 declare let 定义的 jQuery 类型,允许修改这个全局变量
jQuery = function(selector) {
	return document.querySelector(selector);
};
3.2 declare function

declare function 用来定义全局函数的类型。jQuery 其实就是一个函数,所以也可以用 function 来定义:

// src/jQuery.d.ts
declare function jQuery(selector: string): any;
// src/index.ts
jQuery('#foo');
3.3 declare class

当全局变量是一个类的时候,我们用 declare class 来定义它的类型

// src/Animal.d.ts
declare class Animal {    
	name: string;    
	constructor(name: string);    
	sayHi(): string;
}
// src/index.ts
let cat = new Animal('Tom');
3.4 declare enum

使用 declare enum 定义的枚举类型也称作外部枚举(Ambient Enums),举例如下

// src/Directions.d.ts
declare enum Directions {    
	Up,    
	Down,    
	Left,    
	Right
}
// src/index.ts
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

与其他全局变量的类型声明一致,declare enum 仅用来定义类型,而不是具体的值。

Directions.d.ts 仅仅会用于编译时的检查,声明文件里的内容在编译结果中会被删除。它编译结果是:

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

其中 Directions 是由第三方库定义好的全局变量。

你可能感兴趣的:(TypeScript)