5 - TypeScript 的概念和语法

5 - TypeScript 的概念和语法


一、什么是 TypeScript

1. TypeScript 的历史

  • 2012-10 微软发布了 TypeScript 的第一个版本
  • Angular、 React 和 Vue 3.0 都官方支持了 TypeScript
  • Visual Studio Code 完美兼容 TypeScript

2. 为什么选择 TypeScript

  • TypeScript 是添加了类型系统的 JavaScript

  • TypeScript 是 JavaScript 的超集

    • 完全兼容 JavaScript 特性,支持共存

    • 支持渐进式引入与升级

  • TypeScript 是静态类型语言,可以在编译阶段提供类型检查

    • 静态类型的优势
      • 增强代码可读性:基于语法解析 TSDoc,IDE 可以提供增强
      • 增强代码可维护性:在编译阶段暴露大部分错误,节省 debug 实现

    动态类型在执行阶段匹配类型,静态类型在编译阶段匹配类型

    关于动静类型和强弱类型的定义可参考:辨析编程语言的四种类型:动静类型与强弱类型


二、TypeScript 的语法

1. 基础数据类型

TypeScript 作为静态类型语言,其需要在定义变量时指定变量的类型

const q: string = "string";		 //字符串
const w: number = 1;			 //数字
const e: boolean = true;		 //布尔值
const r: null = null;	   		 //null
const t: undefined = undefined;	 //undefined

使用<varName>: <varType> = <value>来声明变量

2. 对象类型

2.1 定义接口

TypeScript 中使用接口(interface)来定义对象的类型和内容

interface IByteDance {
    readonly jobId: number;
    name: string;
    sex: 'man' | 'woman' | 'other';
    age: number;
    hobby?: string;
    [key: string]: any;
}

readonly :定义该属性为只读,即在对象初始化外之外不可被赋值es

sex: 'man' | 'woman' | 'other':定义sex属性,仅能为该三个选项之一

hobby?:定义hobby属性为可选属性,可以不定义该属性

[key: string]: any:任意属性,并约束所有对象属性都必须是该属性的子类型

  • any类型将会跳过编译时类型检查,等到运行时再确定具体的类型
2.2 定义对象

透过将对象类型指向接口来实现对象的类型和内容预定义

const byteDancer: IByteDance = {jobId: 123, name: "Bob", sex: "man", age: 15}

定义byteDancer对象的类型为IByteDance

未定义可选属性hobby

2.3 修改对象
2.3.1 只读属性
byteDancer.jobId = 456;

TS2540: Cannot assign to 'jobId' because it is a read-only property.

只读属性,无法修改

2.3.2 任意属性
byteDance.platform = "data";

任意属性标注下可以添加任意属性

2.3.3 缺少属性
const byteDancer2: IByteDance = {jobId: 123, sex: "man", age: 15}

TS2741: Property 'name' is missing in type '{ age: number; jobId: number; sex: "man"; }' but required in type 'IByteDance'.

缺少属性namebobby可缺省

3.函数类型

TypeScript 中使用function funcName(): <returnType> { ... }来定义函数类型

3.1 带参数函数
function mult(x: number, y: number): string {
    return (x * y).toString();
}

定义函数mult,包含参数xy,均为number类型,函数的返回值为string类型

3.2 可选参数
function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + " " + lastName;
    }
    else {
        return firstName;
    }
}

lastName?:可选参数lastName

3.3 默认参数
function calculate_discount(price: number, rate: number = 0.50) {
    let discount = price * rate;
    console.log("Result: ", discount);
    return discount;
}

rate: number = 0.50:定义参数rate的类型为number,默认值为0.50

3.4 Lambda 函数(箭头函数)
3.4.1 基于 interface 实现
interface Imult {
    (x: number, y: number): string;
}
const mult: Imult = (x, y) => (x * y).toString()
3.4.2 常规实现
const mult: (x: number, y: number) => string = (x, y) => (x * y).toString()
3.5 函数重载

用来实现不同参数输入对应不同参数输出的函数。

需要定义多个重载签名,一个实现签名,一个函数体构造。

let suits = ["hearts", "spades", "clubs", "diamonds"];
// 定义重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
// 定义实现签名
function greet(person: unknown): unknown {
    if (typeof person === 'string') {
        return `Hello, ${person}!`;
    } else if (Array.isArray(person)) {
        return person.map(name => `Hello, ${name}!`);
    }
    throw new Error('Unable to greet');
}
console.log(greet(suits[0]));
console.log(greet(suits));

参考资料

4. 数组类型

TypeScript 中通常使用类型[]Array<>来表示一个数组

// 类型[] 表示
type IArr1 = number[];
// 泛型表示
type IArr2 = Array<string | number>;
// 元组表示
type IArr3 = [number, number, number, string];
// 接口表示
interface IArr4 {[key: number]: any;}

const arr1: IArr1 = [1, 2, 3]
const arr2: IArr2 = ["Bob", 123, "123"]
const arr3: IArr3 = [123, 123, "abc", "abc"]
const arr4: IArr4 = ["string", () => null, {}, []]

number[]:由数字组成的数组

Array<string | number>:使用Array关键字定义数组,由stringnumber类型组成

[number, number, string, string]:前两项为数字,后两项为字符串

5. 空类型

空类型,表示无赋值

type IEmptyFunction = () => void;

6. 任意类型

任意类型,所有类型的子类型

type IAnyType = any;

7. 枚举类型

枚举类型,对标准数组类型的补充,支持枚举值到枚举名的正、反向映射,可以手动为元素编号

enum EColor {Mon, Tue, Wed, Thu, Fri, Sat, Sun}
console.log(EColor['Tue']);	//1
console.log(EColor[1]);		//Tue

这将会把Mon自动赋值为0Sun自动赋值为6

enum EColor {Mon = 1, Tue, Wed, Thu, Fri, Sat, Sun}

这将会把Mon赋值为1Sun自动赋值为7

enum EColor {Mon = -2, Tue, Wed, Thu, Fri, Sat, Sun}

这将会把Mon赋值为-2Sun自动赋值为4

enum EColor {Mon = 2, Tue = 3, Wed = 5, Thu = 8}
console.log(EColor['Tue']);	//3
console.log(EColor[5]);		//Wed

也可以像这样对每一个元素手动赋值

8. 泛型

8.1 什么是泛型

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

8.2 使用泛型
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']
createArray(3, 20);		// [20, 20, 20]

这个函数被用来创建一个数组,给定两个参数(填充数量,填充内容)

这里我们预期这个函数的value参数应该接受一个未知类型T的值,返回一个仅包含类型为T的元素的数组

createArray<T>:定义这个函数将要接受一个未知类型T的参数

value: T:定义value参数的类型为T

Array<T>:定义元素类型为T的数组

8.3 多个类型参数

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

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

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

由于在函数内使用泛型时,不知道其是那种类型,不能随意操作其属性或方法。

这时可以使用interface关键字定义接口,并使用extends关键字要求泛型T继承自Lengthwise,并基于此要求T必须包含length属性

interface Lengthwise {
    length: number;
}

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

T extends LengthwiseT继承自Lengthwise,即要求T必须包含length属性

8.5 泛型参数的默认类型

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

<T = string>:定义泛型T的默认类型为string

9. 类型别名

当需要重复使用一个较为复杂的类型时,可以使用type关键字来指定类型别名,即使用一个变量来指代一个复杂类型

type IObjArr = Array<{
    key: string;
    [objKey: string]: any;
}>

10. 类型断言

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

  • 一般使用as关键字来指定类型断言

  • 类型断言的作用主要是告诉编译器如何判断这是什么类型,这并不会体现在生成的JS中

TypeScript 代码:

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

JavaScript 代码:

var someValue = "this is a string";
var strLength = someValue.length;

11 字面量(字符串/数字)

字面量用以指定字符串或数字所必须使用的固定值

type IDomTag = 'html' | 'body' | 'div' | 'span';
type IOddNumber = 1 | 3 | 5 | 7 | 9;

IDomTag必须为'html''body''div''span'其中之一

IOddNumber必须为13579其中之一


三、TypeScript 高级类型

1. 联合类型 & 交叉类型

&:交叉类型(Intersection Types),将多个类型合并为一个类型,包含被合并的类型的所有属性

|:联合类型(Union Types),几种值之一,用|分隔

type IBookItem = {
    author: string;
} & ({
    type: 'history';
    range: string;
} | {
    type: 'story';
    theme: string;
});

此处定义了IBookItem类型,要求必须包含author属性和type属性

同时利用交叉类型,定义type值为'history'则必须有string类型的range属性;定义type值为'story'则必须有string类型的theme属性。

2. 类型保护 & 类型守卫

类型保护和类型守卫是 TypeScripy 自动类型推断的一种应用

2.1 typeof 关键字
let var1: string | number[];

if (typeof var1 === 'string') {
    // var1.length
} else {
    // var1.push()
}

此处使用typeof var1 === 'string'限定了if语句的第一个代码块内,var1的类型必然为string,则 TypeScript 将会自动为该区域提供适用于string类型的代码检查

而开始处let var1: string | number[];规定了var1只能为string类型或数组,则 TypeScript 会自动判定else语句内的var1类型为数组,并提供相关的代码检查

2.2 in 关键字

in 关键字用来判断一个对象内是否存在一个属性

type type1 = {author: string, name: string, };
let type2: type1 = {author: 'a', name: 'b'};
if ('author' in type2) { ... } else { ... }

'author' in type2:判断'author'属性是否存在于对象type2

2.3 instanceof 关键字

instanceof关键字用来判断一个对象是否是某个类的实例

function Person(name) {
    this.name = name;
}

const person = new Person("John");
console.log(person instanceof Person);  // true

四、TypeScript 工程应用

1. webpack

  1. 配置 webpack loader 相关配置

    转换 webpack 无法识别的文件,例如 ts 转 js

  2. 配置 tsconfig.js 文件

  3. 运行 webpack 启动/打包

  4. loader 处理 ts 文件时,会进行编译与类型检查

相关 loader

2. Node.js

Node.js 使用 TSC 编译

image-20230212155730963

  1. 安装 node 和 npm
  2. 配置 tsconfig.js 文件
  3. 使用 npm 安装 tsc
  4. 使用 tsc 运行编译得到的 js 文件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值