闪电速度入门TypeScript——最精炼无废话教程

174 阅读8分钟

详细视频教程

点击观看:哔哩哔哩

01、概述

TS相当于给JS套了一层类型系统,通过TS则可以进行代码的静态类型检查,好处是将类型错误暴露在运行之前。 image.png

2、原始数据类型

我们可以在变量名后面添加冒号和类型来限制当前变量的类型,常见的原始类型的类型名分别为boolean、number、string、undefined、null。一旦确定了类型,就不能再赋值为别的类型,否则就会报错。提示数值类型不能赋值给布尔类型的变量。

let isGreat: boolean = true;
isGreat = 666; // 报错
let examMark: number = 100;
let fullName: string = '零寂编程';
let u: undefined = undefined;
let n: null = null;

03、数组的类型

我们可以通过 类型名+方括号 的形式来定义数组的类型,这里的“number类型”规定了数组中只能放置此类型的元素,如果放置别的类型的元素就会报错。告诉你string类型的元素不能放置到number类型的数组中。另外,通过数组方法追加元素的时候依然受到此规则的限制

let numberArr1: number[] = [1, 2, 3]
let numberArr2: number[] = [1, 2, 3, '零寂'] // 报错
let numberArr3: number[] = []
numberArr3.push('零寂编程'); // 报错

04、元组:

image.png

当我们需要往数组中放置不同类型的元素的时候,可以使用元组,当然元组在JS中是不存在的,其实元组编译后,依然还是数组。元组限制类型的形式是要把类型放置在方括号中。初始化元组的时候,必须规定每个位置的元素类型。元组中元素的个数以及每个位置上的元素类型也必须与限定的类型保持一致,个数不一致或者顺序不一致都会报错。元组也可以使用数组的方法添加新的元素,但是必须是规定类型中的类型。如果添加别的类型,同样会报错,告诉我们必须添加规定类型的其中一种类型。

let tuple01: [string, number] = ['零寂编程', 100, 200]; // 报错
let tuple02: [string, number] = [100, '零寂编程'];
let tuple03: [string, number] = ['零寂编程', 100];
tuple03.push(666);
tuple03.push(true); // 报错

05、对象的类型——接口:

image.png

TS中的接口是用于描述对象的形状的。这里的形状指的是:定义一个对象都包含哪些属性和方法,每个属性的类型,每个方法的参数类型以及返回值类型等。接口只是用于类型的静态检查,不会被编译成JS代码。也就是在编译为JS文件之前,接口的使命就已经完成了。接口格式如下,一般推荐以字母I开头,表明是一个接口。接口声明完成后,就可以通过接口来指定对象的类型了,此时对象中的属性个数以及属性的类型必须与接口保持一致。另外有些属性还可以设置为可选属性,可以在属性后面添加问号,此时这个属性就可以不用添加,但是如果添加,那么类型也需要与规定类型保持一致。另外还可以通过在属性前面添加readonly,来规定此属性是只读属性,被设置为只读属性后,初始化完成后,就不能再修改此属性,而非只读属性则不受影响。

interface IPerson {
  readonly id: number;
  name: string;
  age?: number;
}
let person: IPerson = {
  id: 1,
  name: '零寂'
}
person.id = 2; // 报错
person.name = '零寂编程';

06、函数的类型

函数中类型的限定包括输入和输出两部分,参数后面的冒号规定了参数的类型,小括号后面的冒号规定了函数返回值的类型。调用函数的时候如果不按类型规定传递参数,就会报错,传递参数的个数不一致也会报错。函数的返回值匹配不上也会报错。参数也可以设置为可选,当然设置为可选的参数就可传可不传了。也可以通过接口的形式来设置函数的形状,包括传递的参数类型以及返回值类型,通过小括号将参数以及参数类型括起来,小括号后面再跟上函数返回值的类型。然后就可以用此接口来限定某个变量为此函数类型了。赋值的函数当然就必须要符合此接口中限定的类型规则了。调用此函数的时候依然要符合接口中定义的类型规则,否则就会报错了。

function sum(x: number, y: number, z?: number): number {
  return x + y;
}
sum(1, 2);

interface ISum {
  (a: string, b: string): string;
}
let sum2: ISum;
sum2 = function(a, b) {
  return a + b;
}
sum2('1', '2');

07、类型推论

即使在不明确指定类型的情况下,TS依然会根据当前变量中所保存的值的类型,推断出一个类型。所以当前变量的类型就已经被初始化为布尔类型了,赋值为别的类型自然就会报错。

let isGreat = true;
isGreat = 666; // 报错

08、联合类型

当一个变量需要同时是多种类型的时候,就可以使用联合类型,多种类型用竖线分隔开。对于联合类型,我们可以给变量赋值为联合类型中的某个类型。如果赋值为非联合类型中的类型的话也会报错。

let numberOrString: number | string;
numberOrString = 100;
numberOrString = '零寂编程';
numberOrString = true; // 报错

联合类型一般可以用于限定函数的参数,用于接收一个不确定类型的的参数。在类型不确定的情况下,访问联合类型中的公有属性或方法是可以的,访问非公有属性或方法是会报错的。

function getLength(input: string | number): number {
  input.valueOf;
  input.toString;
  input.length; // 报错
  const str = input as string // 类型断言
  str.length;
  //如果断言成非联合类型中的类型的话,就会报错
  input as boolean // 报错
}

09、类型断言

请看上面的例子,当在不确定当前变量的类型,又需要访问其中一种类型的属性或方法的时候,就可以使用类型断言。类型断言采用as关键字,将变量强制为联合类型中的其中一种,然后就可以访问此种类型下的属性或者方法了

10、泛型

定义函数的时候,通过将占位符T放在一堆尖括号中,来指定一种动态的类型。只所以说是动态的,是因为具体是什么类型,要到函数调用的时候再来指定,定义好动态类型T后就可以在后面使用了。比如将函数的参数设置为T类型,返回值也设置为T类型。这种形式定义的类型就是泛型,含有广泛的类型的意思。调用函数的时候,就可以将泛型指定为各种类型中的其中一种类型了。

function fn<T>(arg: T): T {
  return arg
}
fn(100);
fn('零寂编程');
fn(true);

11、复杂的泛型

让我们再来看一个更复杂一点的泛型的例子。我们要定义一个用来交换数组中两个元素位置的函数swap,这里定义两个动态类型T和U,用逗号隔开,然后用这两个类型来指定参数数组中各元素的类型,接着指定此函数的返回值为互换位置的两个类型,函数内部就可以交换数组中两个元素的位置后返回即可。调用swap函数的时候,对于传入的数组,第一个为数值,第二个为字符串,互换之后,第一个为字符串,第二个为数值。正好和函数的返回值类型对上,所以代码可以正常编译。如果不互换两个元素的位置,那么就跟函数返回值要求的类型格式对不上了,自然就会报错。而且跟函数的调用处是无关的,即使传递的数组中两个元素的类型相同,也还是会报错的。

function swap<T, U>(arr: [T, U]): [U, T] {
  // return [arr[1], arr[0]];
  return [arr[0], arr[1]] // 报错
}
swap([100, '零寂编程']);
swap([100, 200]);

12、泛型约束

泛型约束是用来进一步更具体的定制泛型的,此函数希望返回参数的length属性,函数返回值的类型当然应该为数值。而函数的参数就必须是一个具有length属性的变量。这种情况下,我们就可以来定义一个包含length属性的接口,然后可以使用这个接口来约束泛型T,这里是通过关键字extends加接口来实现泛型约束的。这样就能起到对动态类型T的约束,调用函数的时候传递的参数就必须是具有length属性的参数,字符串和数组都是具有length属性的,甚至我们还可以传递一个具有length属性的对象。如果传递一个没有length属性的数值的话就会报错了。

interface IWithLength {
  length: number;
}
function returnLength<T extends IWithLength>(arg: T): number {
  return arg.length;
}
returnLength('零寂编程')
returnLength(['Python', 'JavaScript', 'Java'])
returnLength({length: 100}) // 传入的参数必须有length属性
returnLength(666); // 报错,因为没有length属性