前言

2022年了,我才开端学 typescript ,晚吗?(7.5k字总结)

其实早在初学前端时,就有大致了解过 typescript ,但后面工作中底子 vue2 开发为主,所以真实能够接触到 typescript 的时机其实并不多。

虽然在某些间歇性踌躇满志的时刻,我也会上X站搜索 typescript最新教程,但都很难坚持过10节视频,又或许刷的时分看到相关文章,我也会麻溜的点赞保藏一条龙,可是到现在也仅仅在我的保藏夹里吃灰。

或许不是我太懒,仅仅这个国际引诱太多,毕竟刷剧刷短视频它不香吗,学习,学个屁!

最近刷看着大伙的年中总结,不是跳槽涨薪了,便是完成了多少 flag。吓得我从床上一个激灵蹦起,垂头看着自己日渐肥硕的肚子,细心想想,这过去的大半年,除了完成日常的工作,我底子回家就开端躺平。

再这么下去可不可,躺平的日子舒坦归舒坦但多少感到有点庸俗,仍是得给自己整点工作干。

拍拍自己的大肚皮,那就从现在开端,就把我那学了又相当于没学的 typescript ,从头整起来吧

什么是 TypeScript

TypeScript 是一种由微软开发的自在和开源的编程言语。它是 JavaScript 的一个超集,而且本质上向这个言语添加了可选的静态类型和根据类的面向方针编程。

简而言之,TypeScript是JavaScript的超集,具有可选的类型并能够编译为纯JavaScript。从技术上讲TypeScript便是具有静态类型的 JavaScript 。

TypeScript优缺陷

优点

  • 增强代码的可维护性,尤其在大型项目的时分作用显著
  • 友好地在编辑器里提示过错,编译阶段就能查看类型发现大部分过错
  • 支撑最新的JavaScript新特特性
  • 周边生态昌盛,vue3已全面支撑 typescript

缺陷

  • 需求必定的学习本钱
  • 和一些插件库的兼容并不是特别完美,如曾经在 vue2 项目里运用 typescript就并不是那么顺利
  • 增加前期开发的本钱,毕竟你需求写更多的代码(可是便于后期的维护)

装置环境

装置typescript

首先,咱们能够新建一个空文件夹,用来学习 ts,例如我在文件夹下新建了个 helloworld.ts

npm install -g  typescript // 大局装置 ts

不记得自己是否现已装置过 typescript 的,能够运用以下指令来验证:

tsc -v

假如呈现版别,则阐明现已装置成功

Version 4.6.3

生成 tsconfig.json 配置文件

tsc --init

执行指令后咱们就能够看到生成了一个 tsconfig.json 文件,里边有一些配置信息,咱们暂时先按下不表

在咱们helloworld.ts文件中,随意写点什么

const s:string = "彼时彼刻,恰如此时此刻";
console.log(s);

控制台执行 tsc helloworld.ts 指令,目录下生成了一个同名的 helloworld.js 文件,代码如下

var s = "彼时彼刻,恰如此时此刻";
console.log(s);

经过tsc指令,发现咱们的typescript代码被转换成了熟悉的js代码

咱们接着执行

node helloworld.js

即可看到输出结果

装置 ts-node

那么经过咱们上面的一通操作,咱们知道了运转tsc指令就能够编译生成一个js文件,可是假如每次改动咱们都要手动去执行编译,然后再经过 node指令才能查看运转结果岂不是太麻烦了。

而 ts-node 正是来解决这个问题的

npm i -g ts-node // 大局装置ts-node

有了这个插件,咱们就能够直接运转.ts文件了

咱们试一下

ts-node helloworld.ts

能够看到咱们的打印结果现已输出

后续咱们的示例都能够经过这个指令来进行验证

接下来咱们就能够正式进入到 typescript 的学习之旅了

TypeScript 基础类型

Boolean 类型

const flag: boolean = true;

Number 类型

const count: number = 10;

String 类型

  let name: string = "树哥";

Enum 类型

枚举类型用于界说数值调集,运用枚举咱们能够界说一些带姓名的常量。 运用枚举能够清晰地表达意图或创立一组有差异的用例。,如周一到周日,方位上下左右等

  • 一般枚举

初始值默许为 0 其他的成员会会按顺序自动增长 能够理解为数组下标

enum Color {
  RED,
  PINK,
  BLUE,
}
const red: Color = Color.RED;
console.log(red); // 0
  • 设置初始值
enum Color {
  RED = 2,
  PINK,
  BLUE,
}
const pink: Color = Color.PINK;
console.log(pink); // 3
  • 字符串枚举
enum Color {
  RED = "赤色",
  PINK = "粉色",
  BLUE = "蓝色",
}
const pink: Color = Color.PINK;
console.log(pink); // 粉色
  • 常量枚举

运用 const 关键字修饰的枚举,常量枚举与一般枚举的差异是,整个枚举会在编译阶段被删除 咱们能够看下编译之后的作用

const enum Color {
  RED,
  PINK,
  BLUE,
}
const color: Color[] = [Color.RED, Color.PINK, Color.BLUE];
console.log(color); //[0, 1, 2]
//编译之后的js如下:
var color = [0 /* RED */, 1 /* PINK */, 2 /* BLUE */];
// 能够看到咱们的枚举并没有被编译成js代码 仅仅把color这个数组变量编译出来了

Array 类型

对数组类型的界说有两种方式:

  const arr: number[] = [1,2,3];
  const arr2: Array<number> = [1,2,3];

元组(tuple)类型

上面数组类型的方式,只能界说出内部全为同种类型的数组。对于内部不同类型的数组能够运用元组类型来界说

元组( Tuple )表明一个已知数量和类型的数组,能够理解为他是一种特殊的数组

  const tuple: [number, string] = [1, "zhangmazi"];

需求留意的是,元组类型只能表明一个已知元素数量和类型的数组,长度已指定,越界拜访会提示过错。例如,一个数组中或许有多种类型,数量和类型都不确认,那就直接any[]。

undefined和null

默许状况下 null 和 undefined 是一切类型的子类型。 也便是说你能够把 null 和 undefined 赋值给其他类型。

  let a: undefined = undefined;
  let b: null = null;
  let str: string = 'zhangmazi';
  str = null; // 编译正确
  str = undefined; // 编译正确

假如你在tsconfig.json指定了”strictNullChecks”:true ,即开启严厉方式后, null 和 undefined 只能赋值给 void 和它们各自的类型。 (这儿感谢评论区指出) null 和 undefined 只能给它们自己的类型赋值

// 启用 --strictNullChecks
let x: number;
x = 1; // 编译正确
x = undefined;    // 编译过错
x = null;    // 编译过错

可是 undefined 能够给 void 赋值

let c:void = undefined // 编译正确
let d:void = null // 编译过错

any 类型

any会跳过类型查看器对值的查看,任何值都能够赋值给any类型

  let value: any = 1;
  value = "zhangmazi"; // 编译正确
  value = []; // 编译正确
  value = {};// 编译正确

void 类型

void 意思便是无效的, 一般只用在函数上,告知别人这个函数没有回来值。

  function sayHello(): void {
    console.log("hello 啊,树哥!");
  }

never 类型

never 类型表明的是那些永不存在的值的类型。 例如never 类型是那些总是会抛出反常或底子就不会有回来值的函数表达式或箭头函数表达式的回来值类型

值会永不存在的两种状况:

  • 1 假如一个函数执行时抛出了反常,那么这个函数永久不存在回来值(由于抛出反常会直接中断程序运转,这使得程序运转不到回来值那一步,即具有不可达的终点,也就永不存在回来了)
  • 2 函数中执行无限循环的代码(死循环),使得程序永久无法运转到函数回来值那一步,永不存在回来。
// 反常
function error(msg: string): never { // 编译正确
  throw new Error(msg); 
}
// 死循环
function loopForever(): never { // 编译正确
  while (true) {};
}

Unknown 类型

unknown与any相同,一切类型都能够分配给unknown:

  let value: unknown = 1;
  value = "zhangmazi"; // 编译正确
  value = false; // 编译正确

unknown与any的最大差异是:

任何类型的值能够赋值给any,一起any类型的值也能够赋值给任何类型。unknown 任何类型的值都能够赋值给它,但它只能赋值给unknown和any

方针类型

这儿所说的方针类型,便是咱们常说的函数、{}、数组、类

object, Object 和 {} 类型

  • object object 类型用于表明一切的非原始类型,即咱们不能把 number、string、boolean、symbol等 原始类型赋值给 object。在严厉方式下,null 和 undefined 类型也不能赋给 object。
let object: object;
object = 1; // 报错
object = "a"; // 报错
object = true; // 报错
object = null; // 报错
object = undefined; // 报错
object = {}; // 编译正确
  • Object

大 Object 代表一切拥有 toString、hasOwnProperty 办法的类型 所以一切原始类型、非原始类型都能够赋给 Object(严厉方式下 null 和 undefined 不能够)

let bigObject: Object;
object = 1; // 编译正确
object = "a"; // 编译正确
object = true; // 编译正确
object = null; // 报错
ObjectCase = undefined; // 报错
ObjectCase = {}; // ok
  • {}

{} 空方针类型和大 Object 相同 也是表明原始类型和非原始类型的调集

在 TypeScript 中,咱们经过 Class 关键字来界说一个类

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  sayHi(): void {
    console.log(`Hi, ${this.name}`);
  }
}

数组

const flag1: number[] = [1, 2, 3];
const flag2: Array<number> = [1, 2, 3];

函数

函数声明

function add(x: number, y: number): number {
  return x + y;
}

函数表达式

const add = function(x: number, y: number): number {
  return x + y;
}

接口界说函数

interface Add {
  (x: number, y: number): number;
}

可选参数

function add(x: number, y?: number): number {
  return y ? x + y : x;
}

默许参数

function add(x: number, y: number = 0): number {
  return x + y;
}

剩余参数

function add(...numbers: number[]): number {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  return sum;
}

函数重载

函数重载或办法重载是运用相同称号和不同参数数量或类型创立多个办法的一种才能。

function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
  return x + y;
}

上面示例中,咱们给同一个函数供给多个函数类型界说,从而完成函数的重载

需求留意的是:

函数重载真实执行的是同名函数最终界说的函数体 在最终一个函数体界说之前全都归于函数类型界说 不能写详细的函数完成办法 只能界说类型

详细示例原理可参阅

类型推论

假如没有清晰的指定类型,那么 TypeScript 会按照类型推论的规矩推断出一个类型。

let x = 1;
x = true; // 报错

上面的代码等价于

let x: number = 1;
x = true; // 报错

经过上述示例咱们能够看出,咱们没有给 x 指定清晰类型的时分,typescript 会推断出 x 的类型是 number。

而假如界说的时分没有赋值,不论之后有没有赋值,都会被推断成 any 类型而彻底不被类型查看:

let x;
x = 1; // 编译正确
x = true; // 编译正确

类型断语

某些状况下,咱们或许比typescript更加清楚的知道某个变量的类型,所以咱们或许期望手动指定一个值的类型

类型断语有两种方式

  • 尖括号写法
let str: any = "to be or not to be";
let strLength: number = (<string>str).length;
  • as 写法
let str: any = "to be or not to be";
let strLength: number = (str as string).length;

非空断语

在上下文中当类型查看器无法判定类型时,能够运用缀表达式操作符 ! 进行断语操作方针是非 null 和非 undefined 的类型,即x!的值不会为 null 或 undefined

  let user: string | null | undefined;
  console.log(user!.toUpperCase()); // 编译正确
  console.log(user.toUpperCase()); // 过错

确认赋值断语

let value:number
console.log(value); // Variable 'value' is used before being assigned.

咱们界说了变量, 没有赋值就运用,则会报错

经过 let x!: number; 确认赋值断语,TypeScript 编译器就会知道该特点会被清晰地赋值。

let value!:number
console.log(value); // undefined 编译正确

联合类型

联合类型用|分隔,表明取值能够为多种类型中的一种

let status:string|number
status='to be or not to be'
status=1

类型别号

类型别号用来给一个类型起个新姓名。它仅仅起了一个新姓名,并没有创立新类型。类型别号常用于联合类型。

type count = number | number[];
function hello(value: count) {}

穿插类型

穿插类型便是跟联合类型相反,用&操作符表明,穿插类型便是两个类型有必要存在

interface IpersonA{
  name: string,
  age: number
}
interface IpersonB {
  name: string,
  gender: string
}
let person: IpersonA & IpersonB = { 
    name: "师爷",
    age: 18,
    gender: "男"
};

person 即是 IpersonA 类型,又是 IpersonB 类型

留意:穿插类型取的多个类型的并集,可是假如key相同可是类型不同,则该key为never类型

interface IpersonA {
    name: string
}
interface IpersonB {
    name: number
}
function testAndFn(params: IpersonA & IpersonB) {
    console.log(params)
}
testAndFn({name: "黄老爷"}) // error TS2322: Type 'string' is not assignable to type 'never'.

类型护卫

类型维护是可执行运转时查看的一种表达式,用于保证该类型在必定的范围内。 换句话说,类型维护能够保证一个字符串是一个字符串,虽然它的值也能够是一个数值。类型维护与特性检测并不是彻底不同,其主要思维是尝试检测特点、办法或原型,以确认怎么处理值。

换句话说:类型护卫是运转时查看,保证一个值在所要类型的范围内

现在主要有四种的方式来完成类型维护:

  • 1、in 关键字
interface InObj1 {
    a: number,
    x: string
}
interface InObj2 {
    a: number,
    y: string
}
function isIn(arg: InObj1 | InObj2) {
    // x 在 arg 打印 x
    if ('x' in arg) console.log('x')
    // y 在 arg 打印 y
    if ('y' in arg) console.log('y')
}
isIn({a:1, x:'xxx'});
isIn({a:1, y:'yyy'});
  • 2、typeof 关键字
function isTypeof( val: string | number) {
  if (typeof val === "number") return 'number'
  if (typeof val === "string") return 'string'
  return '啥也不是'
}

typeof 只支撑:typeof ‘x’ === ‘typeName’ 和 typeof ‘x’ !== ‘typeName’,x 有必要是 ‘number’, ‘string’, ‘boolean’, ‘symbol’。

  • 3、instanceof
function creatDate(date: Date | string){
    console.log(date)
    if(date instanceof Date){
        date.getDate()
    }else {
        return new Date(date)
    }
}
  • 4、自界说类型维护的类型谓词
function isNumber(num: any): num is number {
    return typeof num === 'number';
}
function isString(str: any): str is string{
    return typeof str=== 'string';
}

接口

咱们运用接口来界说方针的类型。接口是方针的状态(特点)和行为(办法)的抽象(描绘)

简单理解便是:为咱们的代码供给一种约定

咱们运用关键字interface来声明接口

interface Person {
    name: string;
    age: number;
}
let tom: Person = {
    name: 'Tom',
    age: 25
};

咱们界说了一个接口 Person,接着界说了一个变量 tom,它的类型是 Person。这样,咱们就束缚了 tom 的形状有必要和接口 Person 共同。

接口一般首字母大写。(当然挺多人也习惯 I 大写字母最初,用来表明这是一个接口)

设置接口可选|只读

interface Person {
  readonly name: string;
  age?: number;
}
  • 可选特点,咱们最常见的运用状况是,不确认这个参数是否会传,或许存在。

  • 只读特点用于束缚只能在方针刚刚创立的时分修改其值。此外 TypeScript 还供给了 ReadonlyArray 类型,它与 Array 相似,仅仅把一切可变办法去掉了,因此能够保证数组创立后再也不能被修改。

索引签名

有时分咱们期望一个接口中除了包括必选和可选特点之外,还允许有其他的恣意特点,这时咱们能够运用 索引签名 的方式来满意上述要求。

需求留意的是,一旦界说了恣意特点,那么确认特点和可选特点的类型都有必要是它的类型的子集

interface Person {
  name: string;
  age?: number;
  [prop: string]: any; //  prop字段有必要是 string类型 or number类型。 值是any类型,也便是恣意的
}
const p1:Person = { name: "张麻子" };
const p2:Person = { name: "树哥", age: 28 };
const p3:Person = { name: "汤师爷", sex: 1 }

咱们规则 以 string 类型的值来索引,索引到的是一个 any 类型的值

接口与类型别号的差异

实践上,在大多数的状况下运用接口类型和类型别号的作用等价,可是在某些特定的场景下这两者仍是存在很大差异。

TypeScript 的中心原则之一是对值所具有的结构进行类型查看。 而接口的作用便是为这些类型命名和为你的代码或第三方代码界说数据模型。

type(类型别号)会给一个类型起个新姓名。 type 有时和 interface 很像,可是能够作用于原始值(底子类型),联合类型,元组以及其它任何你需求手写的类型。起别号不会新建一个类型 – 它创立了一个新姓名来引用那个类型。给底子类型起别号一般没什么用,虽然能够做为文档的一种方式运用。

接口和类型别号都能够用来描绘方针或函数的类型,仅仅语法不同

type MyTYpe = {
  name: string;
  say(): void;
}
interface MyInterface {
  name: string;
  say(): void;
}

都允许扩展

  • interface 用 extends 来完成扩展
interface MyInterface {
  name: string;
  say(): void;
}
interface MyInterface2 extends MyInterface {
  sex: string;
}
let person:MyInterface2 = {
  name:'树哥',
  sex:'男',
  say(): void {
    console.log("hello 啊,树哥!");
  }
}
  • type 运用 & 完成扩展
type MyType = {
  name:string;
  say(): void;
}
type MyType2 = MyType & {
  sex:string;
}
let value: MyType2 = {
  name:'树哥',
  sex:'男',
  say(): void {
    console.log("hello 啊,树哥!");
  }
}

不同点

  • type能够声明底子数据类型别号/联合类型/元组等,而interface不可
// 底子类型别号
type UserName = string;
type UserName = string | number;
// 联合类型
type Animal = Pig | Dog | Cat;
type List = [string, boolean, number];
  • interface能够兼并声明,而type不可
interface Person {
  name: string
}
interface Person {
  age: number
}
// 此时Person一起具有name和age特点

泛型

泛型是指在界说函数、接口或类的时分,不预先指定详细的类型,而在运用的时分再指定类型的一种特性。

举个比方,比方咱们现在有个这样的需求,咱们要完成一个这样的函数,函数的参数能够是任何值,回来值便是将参数原样回来,而且参数的类型是 string,函数回来类型就为 string?

你很简单写下:

function getValue(arg:string):string  {
  return arg;
}

现在需求有变,需求回来一个 number 类型的值,你会说,联合类型就完事了:

function getValue(arg:string | number):string | number  {
  return arg;
}

可是这样又有一个问题,便是假如咱们需求回来一个 boolean 类型,string 数组乃至恣意类型呢,难道有多少个就写多少个联合类型?

是的,咱们直接用 any 就行了!

function getValue(arg:any):any  {
  return arg;
}

虽然 any 大法好,很多时分 any 也的确能够解决不少问题,可是这样也不符合咱们的需求了,传入和回来都是 any 类型,传入和回来并没有一致

作为一个骚有最求的程序员,咱们还能不能有其他解决办法呢?

这个时分就要祭出咱们的泛型了

底子运用

泛型是指在界说函数、接口或类的时分,不预先指定详细的类型,而在运用的时分再指定类型的一种特性

上面的需求,咱们假如用泛型来解决的话:

function getValue<T>(arg:T):T  {
  return arg;
}

泛型的语法是尖括号 <> 里边写类型参数,一般用 T 来表明第一个类型变量称号,其实它能够用任何有效称号来代替,比方咱们用NIUBI也是编译正常的

泛型就像一个占位符一个变量,在运用的时分咱们能够将界说好的类型像参数相同传入,原封不动的输出

运用

咱们有两种方式来运用:

    1. 界说要运用的类型,比方:
getValue<string>('树哥'); // 界说 T 为 string 类型
    1. 利用 typescript 的类型推断,比方:
getValue('树哥') // 自动推导类型为 string

多个参数

其实并不是只能界说一个类型变量,咱们能够引进期望界说的任何数量的类型变量。比方咱们引进一个新的类型变量 U

function getValue<T, U>(arg:[T,U]):[T,U] {
  return arg;
}
// 运用
const str = getValue(['树哥', 18]);

2022年了,我才开端学 typescript ,晚吗?(7.5k字总结)
typescript 给咱们自动推断出输入、回来的类型

泛型束缚

在函数内部运用泛型变量的时分,由于事先不知道它是哪种类型,所以不能随意的操作它的特点或办法:

function getLength<T>(arg:T):T  {
  console.log(arg.length); // 报错,不能调用 length 特点
}

由于泛型 T 不必定包括特点 length,那么我想 getLength 这个函数只允许传入包括 length 特点的变量,该怎么做呢

这时,咱们能够运用extends关键字来对泛型进行束缚

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

运用:

const str = getLength('树哥')
const arr = getLength([1,2,3])
const obj = getLength({ length: 5 })

这儿能够看出,不论你是 str,arr 仍是obj,只要具有 length 特点,都能够

详细参阅轻松拿下 TS 泛型

泛型接口

在界说接口的时分指定泛型

interface KeyValue<T,U> {
  key: T;
  value: U;
}
const person1:KeyValue<string,number> = {
  key: '树哥',
  value: 18
}
const person2:KeyValue<number,string> = {
  key: 20,
  value: '张麻子'
}

泛型类

class Test<T> {
  value: T;
  add: (x: T, y: T) => T;
}
let myTest = new Test<number>();
myTest.value = 0;
myTest.add = function (x, y) {
  return x + y;
};

泛型类型别号

type Cart<T> = { list: T[] } | T[];
let c1: Cart<string> = { list: ["1"] };
let c2: Cart<number> = [1];

泛型参数的默许类型

咱们能够为泛型中的类型参数指定默许类型。当运用泛型时没有在代码中直接指定类型参数,从实践值参数中也无法推测出时,这个默许类型就会起作用。有点 js 里函数默许参数的意思。

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

泛型东西类型

  • typeof

关键词除了做类型维护,还能够从完成推出类型,

//先界说变量,再界说类型
let p1 = {
  name: "树哥",
  age: 18,
  gender: "male",
};
type People = typeof p1;
function getName(p: People): string {
  return p.name;
}
getName(p1);
  • keyof

能够用来获取一个方针接口中的一切 key 值

interface Person {
  name: string;
  age: number;
  gender: "male" | "female";
}
type PersonKey = keyof Person; //type PersonKey = 'name'|'age'|'gender';
function getValueByKey(p: Person, key: PersonKey) {
  return p[key];
}
let val = getValueByKey({ name: "树哥", age: 18, gender: "male" }, "name");
console.log(val); // 树哥
  • in

用来遍历枚举类型:

type Keys = "a" | "b" | "c"
type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
  • infer

在条件类型句子中,能够用 infer 声明一个类型变量而且对它进行运用。

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;

infer R 便是声明一个变量来承载传入函数签名的回来值类型,简单说便是用它取到函数回来值的类型方便之后运用。

  • extends

有时分咱们界说的泛型不想过于灵活或许说想继承某些类等,能够经过 extends 关键字添加泛型束缚。

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

现在这个泛型函数被界说了束缚,因此它不再是适用于恣意类型:

loggingIdentity(3);  // Error, number doesn't have a .length property

当咱们传入合法的类型的值,即包括 length 特点的值时:

loggingIdentity({length: 10, name: '张麻子'}); // 编译正确
  • 索引拜访操作符

运用 [] 操作符能够进行索引拜访:

interface Person {
  name: string;
  age: number;
}
type x = Person["name"]; // x is string

内置东西类型

  1. Required

将类型的特点变成必选

interface Person {
    name?: string,
    age?: number,
    hobby?: string[]
}
const user: Required<Person> = {
    name: "树哥",
    age: 18,
    hobby: ["code"]
}
  1. Partial

与 Required 相反,将一切特点转换为可选特点

interface Person {
    name: string,
    age: number,
}
const shuge:Person = {
  name:'树哥'
} // error  Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.

从上面知道,假如必传而咱们少穿传了的话,就会报错

咱们运用 Partial 将其变为可选

type User = Partial<Person>
const shuge: User={
  name:'树哥'
} // 编译正确
  1. Exclude

Exclude<T, U> 的作用是将某个类型中归于另一个的类型移除掉,剩余的特点构成新的类型

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
  1. Extract

和 Exclude 相反,Extract<T,U> 从 T 中提取出 U。

type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () =>void

适用于:并集类型

  1. Readonly

把数组或方针的一切特点值转换为只读的,这就意味着这些特点不能被从头赋值。

interface Person {
  name: string;
  age: number;
  gender?: "male" | "female";
}
let p: Readonly<Person> = {
  name: "hello",
  age: 10,
  gender: "male",
};
p.age = 11; // error  Cannot assign to 'age' because it is a read-only property.
  1. Record

Record<K extends keyof any, T> 的作用是将 K 中一切的特点的值转化为 T 类型。

type Property = 'key1'|'key2'
type Person = Record<Property, string>;
const p: Person = {
  key1: "hello 啊",
  key2: "树哥",
};
  1. Pick

从某个类型中挑出一些特点出来

type Person = {
  name: string;
  age:number;
  gender:string
}
type P1 = Pick<Person, "name" | "age">; // { name: string; age: number; }
const user:P1={
  name:'树哥',
  age:18
}
  1. Omit

与Pick相反,Omit<T,K> 从T中取出除掉K的其他一切特点。

interface Person {
  name: string,
  age: number,
  gender: string
}
type P1 = Omit<Person, "age" | "gender">
const user:P1  = {
  name: '树哥'
}
  1. NonNullable

去除类型中的 nullundefined

type P1 = NonNullable<string | number | undefined>; // string | number
type P2 = NonNullable<string[] | null | undefined>; // string[]
  1. ReturnType

用来得到一个函数的回来值类型

type Func = (value: string) => string;
const test: ReturnType<Func> = "1";
  1. Parameters

用于获得函数的参数类型所组成的元组类型。

type P1 = Parameters<(a: number, b: string) => void>; // [number, string]
  1. InstanceType

回来结构函数类型T的实例类型

class C {
  x = 0;
  y = 0;
}
type D = InstanceType<typeof C>;  // C

tsconfig.json

在文章最初环境装置部分,记得咱们有生成一个 tsconfig.json 文件,那么这个文件究竟有什么用呢

tsconfig.json 是 TypeScript 项目的配置文件。

tsconfig.json 包括 TypeScript 编译的相关配置,经过更改编译配置项,咱们能够让 TypeScript 编译出 ES6、ES5、node 的代码。

重要字段

  • files – 设置要编译的文件的称号;
  • include – 设置需求进行编译的文件,支撑途径方式匹配;
  • exclude – 设置无需进行编译的文件,支撑途径方式匹配;
  • compilerOptions – 设置与编译流程相关的选项。

compilerOptions 选项

{
  "compilerOptions": {
    /* 底子选项 */
    "target": "es5",                       // 指定 ECMAScript 方针版别: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定运用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包括在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的过错
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件兼并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的一切的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助东西函数
    "isolatedModules": true,               // 将每个文件做为独自的模块 (与 'ts.transpileModule' 相似).
    /* 严厉的类型查看选项 */
    "strict": true,                        // 启用一切严厉类型查看选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严厉的 null 查看
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时分,生成一个过错
    "alwaysStrict": true,                  // 以严厉方式查看每个模块,并在每个文件里参加 'use strict'
    /* 额外的查看 */
    "noUnusedLocals": true,                // 有未运用的变量时,抛出过错
    "noUnusedParameters": true,            // 有未运用的参数时,抛出过错
    "noImplicitReturns": true,             // 并不是一切函数里的代码都有回来值时,抛出过错
    "noFallthroughCasesInSwitch": true,    // 报告 switch 句子的 fallthrough 过错。(即,不允许 switch 的 case 句子贯穿)
    /* 模块解析选项 */
    "moduleResolution": "node",            // 挑选模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块称号的基目录
    "paths": {},                           // 模块名到根据 baseUrl 的途径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表明项目运转时的结构内容
    "typeRoots": [],                       // 包括类型声明的文件列表
    "types": [],                           // 需求包括的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默许导出的模块中默许导入。
    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的方位
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的方位
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求一起设置了 --inlineSourceMap 或 --sourceMap 特点
    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装修器
    "emitDecoratorMetadata": true          // 为装修器供给元数据的支撑
  }
}

往期回忆

vue 项目开发,我遇到了这些问题
关于首屏优化,我做了哪些

参阅

一份不可多得的 TS 学习指南
TS中文文档
2021 typescript史上最强学习入门文章