请查看 附件,ppt 以及示例代码。title 处 #数字 表示对应示例代码片段。
为什么学习TS
- 因为大家都在用,React、Vue、Angular、Antd、Element-UI、Mobx、Redux…
- 因为大家都在学,既是一个前端的趋势也是提升扩展个人技术的不错方法。
为什么需要使用TS
通过引入强类型系统,补充JS的短板。原生JS类型的灵活性导致在实际环境中可能会出现各种不确定的bug。通过使用TS,我们可以在开发阶段既预测这些可能由类型转换导致的错误,从而提前规避bug,开发出更健全的程序。
TypeScript 的安装与基础使用
安装:
npm i typescript -g
使用:
- tsc { ptah }/{ fileName },可以直接将
.ts
文件 编译为.js
文件 - tsc -- init , ⽣成项⽬配置:
tsconfig.json
,通过配置文件可以设置.ts
文件
TypeScript 的基本类型
boolean:
同 js 布尔值类型,true / false。
string:
同 js 字符串类型,使⽤单引号、双引号、模版字符串。
number:
数字类型,⼆进制、⼋进制、⼗进制、⼗六进制。
null、undefined:
null,undefined :同js值类型,默认是所有类型的⼦类型所以,可以给任意类型的变量赋值null
、undefined
any:
定义:任意值类型,可以赋值任意值类型,注意这里与 null,undefined有区别,null,undefined 是所有类型的子类型,表明它是所有类型的子集,而 any 类型则是:“所有类型都是 any 类型的子集”。
代码语言:txt复制let baz: string = "baz";
let bar: number = 1;
let qux: boolean = false;
let foo: any = 1;
let norf: null = null;
qux = null;
qux = undefined;
foo = "foo"
void:
定义:无返回值的值类型,可以理解为 undefined 类型的子类型。作为一个类型,它也可以当作函数参数类型限定
代码语言:txt复制button.onclick = () => void doSomething();
function doSomething(callback: () => void) {
let foo = callback();
}
never:
定义:表示永远不存在的值类型,比如:程序运行报错,程序陷入了无线循环。
代码语言:txt复制// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
Symbol 类型 当前并无支持
type 类型别名
定义:给⼀个类型起⼀个新的名字,使⽤ type 作为声明关键字。常用于复合类型数据变量的类型声明。 对象类型约定使用大写字母开头 。type
声明的类型,里面包含的属性必须刚好全部满足,不能多也不能少,否则编译将报错,可选属性除外。
type Name = string;
const myName: Name = "foo";
type Age = number;
const myAge: Age = 18;
// 声明复杂类型
// 类型声明里的属性必须存在,可选属性声明前使用: "?"
type User = {
name: string;
sex: string;
age: number;
adult: boolean;
isProgramer?: boolean; // isProgramer是可选属性
};
let foo: User = {
name: 'foo',
sex: "man",
age: 18,
adult: true,
isProgramer: true
};
/*
// Error
let bar: User = {
weight: 100
}
*/
interface 接口
定义: 对⾏为或是事物的抽象,进⾏描述。具体⾏为由类(function/class)去实现 (implement)。
举例:招募一个鸭子合唱团,招募要求是会:呱呱呱的叫声。这时候如果有一只公鸡,并且它的叫声是 “呱呱呱” 那么它便可以被招募,可以成为鸭子合唱团中的一员,或者说可以被当作鸭子。所以 interface 只是对某一类事物进行描述,如果一个具体事物拥有这样的属性, 那么它就是这一类型。 同类型别名 type 属性声明,可以使用 ?
声明可选属性。
// 定义接口
interface Person {
sex: string;
age?: number;
}
// 数据直接赋值
let bar: Person = {
sex: "man",
age: 18,
}
// 对接口进行实现
class Chinese implements Person{
name: string;
}
// 构造调用与赋值
const wilde = new Chinese();
wilde.name = "mouguiding";
注意:类型别名 type 与接口interface 都可以声明复合类型类型的变量, 他们同样可以使用 readonly 来声明只读类型的属性, 使用 ?
来声明可选属性,但是他们存在一定区别。
type 与 interface 的异同
- 都可以描述一个对象或者函数
- 都允许扩展
- type 可以声明基础类型别名,联合类型,元组等。 interface不可以
- interface 可以声明合并。
{
// 描述一个对象或是函数
{
interface User {
name: string
age: number
}
interface SetUser {
(name: string, age: number): void;
}
}
{
type User = {
name: string
age: number
};
type SetUser = (name: string, age: number)=> void;
}
// 扩展
{
interface Name {
name: string;
}
interface User extends Name {
age: number;
}
}
{
type Name = {
name: string;
}
type User = Name & { age: number };
}
{
type Name = {
name: string;
}
interface User extends Name {
age: number;
}
}
{
interface Name {
name: string;
}
type User = Name & {
age: number;
}
}
// 不同点
// type可以 interface 不行的
{
// 基本类型别名
type Name = string
// 联合类型
interface Dog {
wang();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
}
// interface 可以 type 不可以的, 声明属性合并
{
interface Person {
name: string
age: number
}
interface Person {
sex: string
}
interface Programer extends Person{
skill: string;
}
}
}
Typescript 中数组的类型声明
类型声明使用 Type:[]
或者 Array<Type>
,尖括号<>
用于类型约束
// 声明元素类型为字符串的数组
let foo: string[] = [ “foo”, “bar”, “baz” ]
// 声明元素类型为字符串的数组
let bar: Array<string> = [ “foo”, “bar”, “baz” ]
// bar.push(5) // error
// 声明元素类型为字符串或者数字的数组
let baz: Array<string|number> = [ “foo”, 100 ]
baz.push(10);
Typescript 中对象的类型声明
代码语言:txt复制// 使用 类型别名声明类型
type User = {
name: string;
sex: string;
age: number;
adult: boolean;
isProgramer?: boolean;
};
let foo: User = {
name: 'foo',
sex: "man",
age: 18,
adult: true,
isProgramer: true,
};
// 使用接口 interface 声明类型
interface Person {
readonly name: string;
sex?: string;
age?: number;
}
let bar: Person = {
name: 'bar',
sex: "man",
age: 18,
}
Typescript 中函数的类型声明
函数声明主要涉及到函数参数类型声明以及函数返回值类型限定。
代码语言:txt复制let fun71: any;
fun71 = function(): void {
console.log("I'm fun71");
}
function fun72(): any {
return "any";
};
let fun73: Function;
fun73 = function(): void{
console.log("I'm fun73");
};
let fun74: (str: string) => string;
fun74 = function(name) {
return `I'm ${name}`;
};
type Fun75 = (str: string) => string;
const fun75: Fun75 = function(name) {
return `I'm ${name}`;
};
interface Fun76{
(x: number, y: number): number;
}
const fun76: Fun76 = (x, y) => {
return x y;
}
interface Fun77{
(x: number, y?: number): number;
}
const fun77: Fun77 = (x, y) => {
if (y) return x y;
return x;
}
函数重载
函数重载允许用相同的名字与不同的参数来创造多个函数
- 先提供没有实现的函数定义列表
- 必须提供所有函数组合的实现
function sum(x: number, y: number): number;
function sum(x: number, y: number, z: number): number
function sum(x: number, y: number, z?: number): number {
if (typeof z === 'undefined') {
return x y;
} else {
return x y z;
}
}
let n = sum(1, 2, 3);
console.log(n);
n = sum(1, 2);
console.log(n);
Typescript 高级类型
union 联合类型:
定义:该类型的取值可以是多种类型的⼀种, 使用竖线对多种类型进行联合。每个类型可以是单一类型或复合类型
代码语言:txt复制type RTX = number|string|null;
let foo: RTX = "bar";
foo = 123;
foo = null;
foo = undefined;
interface Dog {
wang();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
Tuple 元组:
定义:数组合并了相同类型的对象,元组则是合并了不同类型的对象,并且指定位置。注意是指定位置,与数组声明不同的是,数字只是限定了类型,不限定长度与位置
代码语言:txt复制interface Dog {
wang();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
// 具体定义数组每个位置的类型, 值类型位置不能变换。
type PetList = [Dog, Pet, Cat];
let dog: Dog = {
wang: () => {}
}
let cat: Cat = {
miao: () => {}
}
let petlist: PetList = [dog, dog, cat]
enum 枚举:
定义: 类型⽤于取值被限定在⼀定范围内的场景 ,⽐如⼀周只能有七天,颜⾊限定为红绿蓝等,值为序号,可⾃定义值。重点:值为序号,相当于数组的下标,而不是本身。
代码语言:txt复制// 它的值是数字序号,从 0 开始
// 代码可读性强
// 可能会常用于下拉框等应用
enum DaysOfTheWeek {
SUN = 100, MON, TUE, WED, THU, FRI, SAT
}
let day: DaysOfTheWeek;
day = DaysOfTheWeek.MON;
if (101 === day) {
console.log("Got to go to work");
} else {
console.log("Don't work");
}
字面量类型:
定义:字面量类型使用竖线直接分割值(PS:联合类型是使用竖线联合类型,这里是值),此变量的值只能从分隔的值其中一个,使用 类型别名 type 搭配 |
声明。
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'
Generics 泛型:
定义:是指在定义函数、接⼝或类的时候,不预先指定具体的类型,⽽在使⽤的时候再指定类型的⼀种特性。泛型不是 any
代码语言:txt复制// 实现一个方法,输入两个参数,第一个是数字 N,
// 第二个是一个简单类型数据,返回重复 N 次的指定类型数组
type CreateArrayType<T> = (x:number, y:T) => T[];
const createArray2:CreateArrayType<number> = (x, y) => {
let result = [];
for(let i=0; i<x; i ){
result[i] = y;
}
return result;
}
createArray2(2, 2);
其他部分
类型约束:
类型约束,常见的为使用<>
进行限定。
如数组声明中:
代码语言:txt复制let arr53: Array<string|number|boolean> = [true, 100, "bar"];
其次如当我们需要返回一个变量的长度时,首先这个变量需要一个长度的属性那么,声明变量类型时它应该拥有长度属性:
代码语言:txt复制// 声明 LengthType 类型,必须包含 length 属性
interface LengthType {
length: number;
}
// T 继承自 LengthType 类型,也就表明它必须具有 length 属性
function get<T extends LengthType>(arg: T): number {
return arg.length;
}
特别是在使用泛型时,因为类型的不确定性导致程序可能会运行报错,善用类型约束可以提前处理这些问题。
类型断言:
可以理解为类型强制转换,就是把一个变量指定为另一个类型。
代码语言:txt复制let foo: any;
let bar = <string>foo; // 现在 bar 的类型是 'string'
interface Foo {
bar: number;
bas: string;
}
const foo = {} as Foo;
// 双重类型断言
function handler(event: Event) {
const element = (event as any) as HTMLElement;
}
类型推论:
定义:TypeScript 会通过变量或返回值等赋值时推导出这个值的类型,如果在随后的代码中又进行了不同类型的值赋值,那么编译会报错:
代码语言:txt复制let foo = 'hello,world';
foo = 9999;
Type '9999' is not assignable to type 'string'.
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查
代码语言:txt复制let foo;
foo = 'bar';
foo = 18; //ok
特别对于联合类型时,类型推论会限制到只能访问联合类型中类型的共有属性。否则将报错。
代码语言:txt复制//正确,因为num都有toString方法
function test(num: string | number) {
return obj .toString()
}
let num = 'server';
console.log(test(num))
//编译错误,number类型的num 不存在length属性
function test(num: string | number) {
return num.length;
}
let num = 'server';
console.log(test(num))