Typescript 知识点

TypeScript

自学typescript 笔记,参考阮一峰老师ts

js:动态弱类型语言

ts:静态类型语言

优势:

  • 有利于代码的静态分析

    不必要运行代码就可以推断错误---静态代码分析

  • 有利于发现错误

    每个值,每个变量都有严格的类型约束,可以及时发现拼写,语法,方法调用错误

  • 更好IDE 支持,可以做到自动语法补全,语法提示

  • 提供代码文档

  • 有助于代码重构

缺点:

  • 丧失了动态类型的代码灵活性。

动态类型有非常高的灵活性,给予程序员很大的自由,静态类型将这些灵活性都剥夺了

  • 增加了编程工作量。

有了类型之后,程序员不仅需要编写功能,还需要编写类型声明,确保类型正确。这增加了不少工作量,有时会显著拖长项目的开发时间。

  • 更高的学习成本。

类型系统通常比较复杂,要学习的东西更多,要求开发者付出更高的学习成本。

  • 引入了独立的编译步骤。

原生的 JavaScript 代码,可以直接在 JavaScript 引擎运行。添加类型系统以后,就多出了一个单独的编译步骤,检查类型是否正确,并将 TypeScript 代码转成 JavaScript 代码,这样才能运行。

  • 兼容性问题。

TypeScript 依赖 JavaScript 生态,需要用到很多外部模块。但是,过去大部分 JavaScript 项目都没有做 TypeScript 适配,虽然可以自己动手做适配,不过使用时难免还是会有一些兼容性问题。

类型声明

类型推断

ts 设计思想是类型声明是可选的,可加可不加

tsc 编译器

全局安装之后可以使用tsc 进行编译

部分参数

  1. --outFile 将多个Typescript 脚本编译 成一个 javascript 文件

    ts 复制代码
    tsc file1.ts file2.ts --outFile app.js
  2. --outDir

    编译结果默认保存在当前目录,--outDir 可以指定存放目录

    ts 复制代码
    tsc app.ts --outDir dist
  3. --target

    tsc 默认将ts 编译为es3 版本,--target 指定编译版本

    ts 复制代码
    tsc --target es2015 app.ts
  4. --noEmitNoError 当代码有错误时 ts 会进行提示,但是还有产生编译产物,如果希望一旦报错就停止编译,不生产编译产物,可以使用

    --noEmitOnError 参数

    ts 复制代码
    tsc --noEmitOnError app.ts
  5. --noEmit

    只检查类型是否有编译错误,不会生成app.js

    ts 复制代码
    tsc --noEmit app.ts

tsconfig.json

ts 允许将tsc 的编译参数,写在配置文件 tsconfig.json 中,tsc 会自动读取该文件

ts 特殊的类型:any、unknown、never 类型

any

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值

ts 复制代码
let x:any
x = 1;
x= 'hello'
x = false

变量类型一旦设置了any,ts 实际上就会关闭类型检查,即使有明显的类型错误,只要句法正确,都不会报错。

由于这个原因,应该尽量避免使用any类型,否则就失去了使用 TypeScript 的意义。

实际开发中,any类型主要适用以下两个场合。

(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any

(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。

总之:any 类型可以看成是所有其他类型的全集,包含了一切可能的类型,typescript 将这种类型称为 顶层类型,意为涵盖了所有下层

TypeScript 提供了一个编译选项noImplicitAny,打开该选项,只要推断出any类型就会报错。

弊端:

  • 关闭了类型检查
  • 污染问题 因为他可以赋值给任意类型的变量

unknown 类型

为了解决 any 类型污染其他类型变量的问题,ts 在3.0版本之后推出了unknown 类型。他与any 含义,表示类型不确定,可能是任意类型,但是他有一些限制,可以视为严格版本的any

与 any 相同之处。在于所有类型都可以分配给unknown 类型

ini 复制代码
let x:unknown;

x = true; // 正确
x = 42; // 正确
x = 'Hello World'; // 正确

不同之处:

他不能直接使用,有一下几个限制:

  1. unknown 类型变量不能赋值给其他类型的变量

    ts 复制代码
    let v:unknown = 123
    let v1:boolean = v // 报错
    let v2:number = v // 报错
  2. 不能直接使用unknown 类型变量的方法属性

typescript 复制代码
let v1:unknown = { foo: 123 };
v1.foo  // 报错

let v2:unknown = 'hello';
v2.trim() // 报错

let v3:unknown = (n = 0) => n + 1;
v3() // 报错
  1. 运算是有限制的,只能进行比较运算,取反运算,typeof 运算,instanceof 运算
ini 复制代码
let a:unknown = 1;

a + 1 // 报错
a === 1 // 正确

怎么才可以使用unknown 类型变量呢? 就是使用"类型缩小",就是判断具体的类型

ts 复制代码
let a:unknown = 1
if(typeof a === 'number'){
	let r = a+10
}
let s:unknown = 'hello';

if (typeof s === 'string') {
  s.length; // 正确
}

这样设计的目的是,只有明确unknown 变量的实际类型,才迅速使用,防止像any 那样可以随意使用 ,污染其他变量

总之 unknown 可以看做更安全的any,一般来说,凡事需要设为any 类型的地方,通常都应该考虑设为unknown 类型,

unknown 类型只可以赋值给any,unknown 类型

unknown 也可以视为所有的其他类型 的全集(除了any),他和any 一样,也属于Ts 顶层类型

never 类型

空值类型 即该类型为空,不包含任何值

使用场景:主要在一些类型运算之中,保证类型运算的完整性,另外,不可能返回值的函数,返回值就可以写成never 类型

typescript 复制代码
function f():never {
  throw new Error('Error');
}
let v1:number = f(); // 不报错
let v2:string = f(); // 不报错
let v3:boolean = f(); // 不报错

never 可以赋值给其他任意其他类型,因为never 是底层类型

基本类型

js 有8中数据类型

  1. string
  2. boolean
  3. number
  4. bigint
  5. object
  6. symbol
  7. undefined
  8. null

其中 原始数据类型为 string boolean number,bigint,引用数据类型为object

null/undefined 是两种独立类型,他们各自都有一个值

undefined 只包含 值undefined 表示未定义

null 表示空值

如果关闭了noImplicitAny 和strictNullChecks 他们类型会被推断为any

包装对象

js 复制代码
'hello',chatAt(1) // e

字符串 hello 执行了chatAt() 方法时,原始数据类型本身乜有方法,这段代码可以执行,因为在调用方法时,字符串会自动转化为"包装对象",chatAt() 方法定义在包装对象上

注意:Symbol ,bigInt 无法直接获取他们的包装对象,因为他们不能作为构造函数直接使用,但是剩下三种可以

  • String()
  • Number()
  • Boolean()

包装对象类型与字面量类型

javascript 复制代码
'hello' // 字面量
new String('hello') // 包装对象

为了区分这两种情况,TypeScript 对五种原始类型分别提供了大写和小写两种类型。

  • Boolean 和 boolean
  • String 和 string
  • Number 和 number
  • BigInt 和 bigint
  • Symbol 和 symbol

大写:包装对象+字面量 小写:字面量

Object 类型和object 类型

Object

大写Object 类型js 广义对象,都可以转化为Object 的值(除了undefinednull)

ini 复制代码
let obj:Object;
 
obj = true;
obj = 'hi';
obj = 1;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;

object

小写代表js 狭义对象只能代表 字面量的对象(数组,对象,函数)

ini 复制代码
let obj:object;
 
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;
obj = true; // 报错
obj = 'hi'; // 报错
obj = 1; // 报错

undefined null

特殊的类型 可以赋值给其他的类型,并不会报错

开启tsc 配置 strictNullChecks undefined 和null 就不能赋值给其他类型的变量

值类型

单个值也是一种类型

ts 在进行类型推断时 ,遇到const 声明的变量,如果代码中没有注明类型,就会推断改变量为值类型

js 复制代码
let x:'hello'
x = 'hello'

const x = 'https' // x 的类型为'https'
const y:string = 'https' // x:string
const x= {foo:1} // x:{foo:number}

联合类型 |

type A = number|string

交叉类型 &

一般用于type (类型别名合并)

ts 复制代码
type A = {foo:number} & {bar:number}

类型别名 type

用来定义类型别名

类型别名不可以重名 类型别名是块级作用域

typeof 运算符

typeof 在js 中是一个 一元运算符,返回一个字符串,代表操作数的类型

在ts 中 typeof 操作的依然是一个值 ,返回的是 该值的类型

类型兼容

ini 复制代码
type T = number|string;

let a:number = 1;
let b:T = a;

a 类型可以赋值给b 类型 ,因为a 类型是b 类型的子类型,也就是说number 类型是 number|string 的子类型

class

类 (class)是面向对象的编程的基本构件,封装了属性和方法

属性类型

tsx 复制代码
class Point{
	x:nubmer;
    y:number
}

注意点:

  • 如果给出初始值,可以不写类型

  • ts 配置项:strictProperInitialization 打开后必须设置初始值,默认是打开的

  • 可以使用非空断言解决上面问题

  • 属性前面增加readonly 修饰符,表示该属性为只读的,在够赞函数中可以修改只读属性值

方法类型

类的方法类型就是普通函数 与声明函数类型一致

存取器方法

getter/setter

kotlin 复制代码
class  C{
	_name = ''
	get name(){
		return this._name
	}
	set name(value){
		this._name = va;ue
	}
}

注意点:

  • 如何只有get 没有set ,那么该属性自动会成为只读属性
  • 5.1 版本之前get、set 参数类型属性值一致
  • get、set 可访问性一致

class 类型

ts 中类本身就是一种类型,代表该类的实例类型,而不是class 的自身类型

js 复制代码
class Color{
    name:string;
    contructor(name:string){
        this.name = name
    }
}

const green:Color = new Color('green')

上面示例中,定义了一个类Color。它的类名就代表一种类型,实例对象green就属于该类型。

类的自身类型:

如果想获取类的自身类型,最简单的方法就是用 typeof

js 复制代码
class PointClass{
    x:number;
	y:number;
    
	contructor(x:number,y:number){
        this.x  = x;
        this.y = y
    }
}

function createPoint(
	    PointClass:typeof Point,
        x:number,
         y:number
):Point{
	return new PointClass(x,y)
}

可访问性修饰符

public、private、protected 这三个修饰符写在属性和方法前面

  • public

    外部实例可以调用

  • private 私有成员,只有当前类的内部可以访问,子类和类的实例都不可以进行访问

    但是 使用 【】 和in 都可以访问到私有成员 ,es2022 引入了# 关键字写法,

    js 复制代码
    class A {
    	#x = 1;
    }
    const a = new A();
    a['x'] // 报错

    构造方法可以是私有的

    js 复制代码
    class Singletion {
    	private static instance?:Singleton;
    	
    	private constructor(){}
    
    	static getInstance(){
    		if(!Singleton.instance){
    			Singletion.instance = new Singleton()
    		}
            return Singleton.instance
    	}
    }
    const s = Singleton.getInstance()
  • protected

    子类可以使用

实例属性的简写形式

ts 复制代码
class Point {
  x:number;
  y:number;

  constructor(x:number, y:number) {
    this.x = x;
    this.y = y;
  }
}
// 简写
class Point {
  constructor(
    public x:number,
    public y:number
  ) {}
}

const p = new Point(10, 10);
p.x // 10
p.y // 10

静态成员

使用static 关键字

静态成员只能通过类本身使用的成员,不能通过实例对象对使用

抽象类 抽象成员

抽象类只能作为基类,不能被实例,只能当做其他类的模板

抽象属性:抽象类可以有已经实现好的属性和方法,也可以还未实现的属性和方法,后者叫做抽象成员

(1)抽象成员只能存在于抽象类,不能存在于普通类。

(2)抽象成员不能有具体实现的代码。也就是说,已经实现好的成员前面不能加abstract关键字。

(3)抽象成员前也不能有private修饰符,否则无法在子类中实现该成员。

(4)一个子类最多只能继承一个抽象类。

this

decalre

decalre 关键字告诉编译器 ,某个类型是存在的,可以在当前文件中使用

作用:让当前文件可以使用其他文件的声明的类型

特点:

  • 只是通知编译器某个类型是存在的,不用给出具体实现
  • 只能描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构

decalre 可以描述以下类型:

  • 变量 const let var
  • type interface
  • class
  • enum
  • function 函数
  • module
  • 命名空间 namespace

decalre 变量

可以给出外部变量的类型描述

例如当前脚本使用了外部脚本定义的全局变量

js 复制代码
x = 123 // 报错

declare let x:number
declare let x:number = 1 // 报错 不可以进行赋值

decalre 类

js 复制代码
declare class C {
  // 静态成员
  public static s0():string;
  private static s1:string;

  // 属性
  public a:number;
  private b:number;

  // 构造函数
  constructor(arg:number);

  // 方法
  m(x:number, y:number):number;

  // 存取器
  get c():number;
  set c(value:number);

  // 索引签名
  [index:string]:any;
}

declare namespace ,module

如果想把变量,函数,类 组织在一起

tsx 复制代码
declare namespace AnimaliLib{
	class Animal{
		construcotr(name:string);
        eat():void;
        sleep():void
	}
    type Animals = 'Fish' | "Dog"
}
declare module AnimalLib{
	class Animal{
		construcotr(name:string);
        eat():void;
        sleep():void
	}
    type Animals = 'Fish' | "Dog"
}   
ts 复制代码
import { Foo as Bar } from 'moduleA';

declare module 'moduleA' {
  interface Bar extends Foo {
    custom: {
      prop1: string;
    }
  }
}

上面示例中,从模块moduleA导入了Foo接口,将其重命名为Bar,并用 declare 关键字为Bar增加一个属性custom

某些第三方模块,原作者没有提供接口类型,可以在自己的脚本顶部加上一行命令

js 复制代码
declare mudule '模块名'

declare module 'hot-new-module'

加上上面的命令以后,外部模块即使没有类型声明,也可以通过编译。但是,从该模块输入的所有接口都将为any类型。

decalre global

为js 引擎的原生对象添加属性和方法,可以用declare global{} 语法

注意:declare global 必须用在模块里面

tsx 复制代码
export {}

declare global{
	interface String{
		toSmalString():string
	}
}

// 实现
  String.prototype.toSmalString = ():string =>{
      return ''
  }
  
 // 例子2
  declare global {
  interface window {
    myAppConfig:object;
  }
}

const config = window.myAppConfig;

declare module 用于类型声明文件

我们可以为每个模块脚本定义一个d.ts 把该脚本用到的类型定义都定义在这个文件里面。但是更方面的做法是为了整个项目,定义一个大的.d.ts文件,在这个里面 使用declare module 定义每个模块脚本的类型

tsx 复制代码
declare module "url" {
  export interface Url {
    protocol?: string;
    hostname?: string;
    pathname?: string;
  }

  export function parse(
    urlStr: string,
    parseQueryString?,
    slashesDenoteHost?
  ): Url;
}

declare module "path" {
  export function normalize(p: string): string;
  export function join(...paths: any[]): string;
  export var sep: string;
}

上面示例中,urlpath都是单独的模块脚本,但是它们的类型都定义在node.d.ts这个文件里面。

使用时,自己的脚本使用三斜杠命令,加载这个类型声明文件。

ini 复制代码
/// <reference path="node.d.ts"/>

如果没有上面这一行命令,自己的脚本使用外部模块时,就需要在脚本里面使用 declare 命令单独给出外部模块的类型。

类型运算符

keyof

接收一个对象类型作为参数,返回该对象的所有键名组成的联合类型

知识点:

  • keyof any 时返回 string|number|symbol 因为对象的键名只有三种类型

  • 对于没有自定义键名的类型,返回never

    typescript 复制代码
    type keyT = keyof object // never
  • 如果对象属性名为索引形式,keyof 会返回属性名的索引类型

    typescript 复制代码
    interface T {
    	[prop:number]:number
    }
    type KeyT = keyof T // number
    // 示例2
    interface T {
    	[prop:string]:number
    }
    type KeyT = keyof T // number | string

    在实例2 中javascript 属性名为字符串时,包含了属性名为数值情况,因为数组属性名会自动转化为字符串

  • keyof 数组或元组时

    js 复制代码
    type Result = keyof ['a', 'b', 'c'];
    // 返回 number | "0" | "1" | "2"
    // | "length" | "pop" | "push" | ···

上面示例中,keyof 会返回数组的所有键名,包括数字键名和继承的键名。

用途:

  1. keyof 运算符往往用于精确表达对象的属性类型
tsx 复制代码
function<Obj,K extends keyof Obj>(obj:Obj,key:K):Obj[K]{
	return obj[key]
}
  1. 做属性映射

    tsx 复制代码
    type NewProps<Obj> = {
    	[Prop in keyof Obj]:boolean
    }
    
    type MyObj = {foo:number}
    
    type NewObj = newProps<MyObj>
    • 去掉修饰符 + 增加修饰符
    tsx 复制代码
    type MyProps<Obj>{
    	[props in keyof Obj]-?:Obj[props]
    }
    
    type MyObj = {
    	foo?:number;
    }
    // {foo:number}
    type NewObj = MyProps<MyObj> 

in

js:判断属性 是否在包含在对象中 ts : 用于遍历 联合类型每一个成员类型

[ ] 运算符

用于取出对象的键值类型

extends ...?: 条件运算符

​ ts 推出了 类似 js 的?:三元运算符 ,但多出了extends 关键字

条件运算符extends...?:可以根据当前类型是否符合某种条件,返回不同的类型。

tsx 复制代码
T extends U ? X : Y

上面的例子中:判断类型T 是否可以赋值 给U 类型,即 T 是U 的子类型,如果是 类型为 X ,否则为 Y

注意点:

如果 需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型

tsx 复制代码
(A | B) extends U?X:Y

//等价于
(A extends U ? X : Y) |(B extends U ? X : Y )

infer

用来推断定义泛型里面推断出来的类型参数,而不是外部传入的类型参数

注意点:

  • 用于在extends 条件判断中

  • 用于条件判断中的为真的 值

is

函数返回值的判定 限定 返回值 和参数之间的关系

js 复制代码
function isFish(
	pet:Fish | Bird
):pet is Fish{
	return (pet as Fish).swim !== undefined
}

类型映射

映射:指将一种类型 安装映射规则,转换成另一种类型 ,通常用于对象类型

举例来说,现有一个类型A和另一个类型B

ts 复制代码
type A = {
  foo: number;
  bar: number;
};

type B = {
  foo: string;
  bar: string;
};

上面示例中,这两个类型的属性结构是一样的,但是属性的类型不一样。如果属性数量多的话,逐个写起来就很麻烦。

使用类型映射,就可以从类型A得到类型B

ts 复制代码
type A = {
  foo: number;
  bar: number;
};

type B = {
  [prop in keyof A]: string;
};

类型工具

  1. Awaited

    取出 Promise 的返回值类型

    ts 复制代码
    // string
    type A = Awaited<Promise<string>> 
  2. ContructorParameters 提前构造方法的 Type 的 参数类型,组成一个元组

ts 复制代码
//  [x:string,y:string]
type T1 = ConstructorParameters<
	new (x:string,y:string) => object
>
// [x?:string | undefined]
type T2 = ConstructorParamters<
	new (x?:string) => object
>
  1. Exclude<UnionType,ExcludeMembers> 从联合类型中删除某些类型成员,组成一个新的类型 返回

    js 复制代码
    type T1 = Exclude<'a'|'b'|'c', 'a'>; // 'b'|'c'
    type T2 = Exclude<'a'|'b'|'c', 'a'|'b'>; // 'c'
    type Exclude<T,U> = T extends U?never:T;
    
    // 首先判断 T 是否兼容 U 是:返回never 类型,(因为never 类型是其他类型的子类型,它跟其他类型组成联合类型时,可以从联合类型中 删除)
  2. Extrace<UnionType,Union>

用来 从联合类型 (UnionType)中,提取指定类型 Union 中,组成一个新类型返回,功能与 Exclude<T,U> 正好相反

ts 复制代码
type T1 = Extract<'a'|'b'|'c','a'> // 'a'
type T2 = Extract<'a'|'b'|'c', 'a'|'b'>; // 'a'|'b'
type T3 = Extract<'a'|'b'|'c', 'a'|'d'>; // 'a'

// 实现
type Extrace<T,U> = T extends U? T : never
  1. InstanceType

    提取 构造函数的返回值的类型

    ts 复制代码
    type T = InstanceType<
    	new () => object
    >

    上面示例中,类型参数是一个构造函数new () => object,返回值是该构造函数的实例类型(object)。

  2. NonNullable

    从联合类型中 Type 删除 null 和 undefined 类型,组成一个新类型返回,也就是返回 Type 的非空类型版本

    ts 复制代码
    type T2 = NonNullable<string[]|null|undefined>; // string[]
  3. Omit<Type,Keys>

    Omit<Type,keys> 用来从对象类型Type 中 删除指定的属性 keys, 组成一个新的对象类型返回

    ts 复制代码
    interface A {
    	x:number
        y:number
    }
    type T1 = Omit<A,'x'> // {y:number}
    type T2 = Omit<A,'y'> // {x:number}

OmitThisParameter

从 函数类型中 移除this 参数
9. Paramters

函数类型 中提取 参数类型

js 复制代码
type T1 = Parameters<() => string>; // []
type T2 = Parameters<(s:string) =>void> // [s:string]
  1. Partial 返回一个新类型,将参数类型Type 的所有属性变为可选属性

    ts 复制代码
    interface A {
    	x:number
        y:number
    }
    
    type T = Partial<A,> // {x?:number,y?:number}
    
    // 具体实现:
    type Partial<T> = {
    	[p in keyof T]?:T[p]
    }
  2. Pick<Type ,Keys> 返回:一个新的对象类型, 第一个参数Type 为一个对象类型,第二个参数 keys 是 Type 里面被选定的键名

    js 复制代码
    interface A {
    	x:number;
    	y:number
    }
    
    type T = Pick<A,x> // {x:number}
    type T1 = Pick<A,'x' | 'y'> // {x:number,y:number}
    
    // 实现:
        Type Pick<T,K extends keyof T> = {
    		[p in k]:T[p]
    	}
  3. Readonly 返回一个新的类型,将参数类型Type 的所有属性变为 只读属性

go 复制代码
```ts
typescript 复制代码
interface A {
	x:number;
	y?:number
}
// {readonly x:number,readonly y?:number}
type T = Readonly<A> 
```
  1. Record<keys,Type> 返回一个对象类型,参数Keys 用作键名,参数Type 用作键值类型
scala 复制代码
```tsx
type T = Record<'a',number> // {a:number}
type T1 = Record<'a'|'b',number> // {a:number,b:number}
type T2 = Record<'a',number|string> // {a:number|string}
```

其中 keys 的类型必须兼容 `string|number|symbol`否则不能用键名,
实现原理:

```ts
type Record<K extends string|number|symbol,T> = {
	[p in K]:T
}
```
  1. Required
python 复制代码
返回一个新的类型:将参数类型Type 的所有属性变为 必选属性,他与Partial 作用相反
js 复制代码
interface A{
	x?:number;
    y:number;
}
type T = Required<A> // {x:number,y:number}

具体实现:

ts 复制代码
type Required<T> = {
    [p in keyof T] -?:T[p];
}

-? 去除可选属性的问号,+? 增加可选属性的问号

  1. ReadonlyArray

    用来生成一个只读数组类型,类型参数Type表示数组成员的类型

    ts 复制代码
    const values:ReadonlyArray<string> = ['a','b','c']
    
    values[0] = 'x'// errOr
    values.push('x') //error

    具体实现:

    ts 复制代码
    interaface ReadonlyArra<T> {
        readonly length:number
        readonly [n:number]:T
    }
  2. ReturnType 提取函数类型Type 的返回值类型

    typescript 复制代码
    type T1 = ReturnType<() => string> //string
    type T2 = ReturnType<() =>{a:string;b:string}> // {a:string;b:string}

    具体实现:

    typescript 复制代码
    type ReturnType<T extends (...args:any) => any> =
    
    T exends (...args:any) => infer R?R:any
复制代码
## `ThisParameterType<Type>`

ThisParameterType<Type>提取函数类型中this参数的类型。

typescript 复制代码
function toHex(this: Number) {
  return this.toString(16);
}

type T = ThisParameterType<typeof toHex>; // number

字符串类型工具

TypeScript 内置了四个字符串类型工具,专门用来操作字符串类型。这四个工具类型都定义在 TypeScript 自带的.d.ts文件里面。

它们的实现都是在底层调用 JavaScript 引擎提供 JavaScript 字符操作方法。

  1. Uppercase<StringType> Uppercase<StringType>将字符串类型的每个字符转为大写。

    ini 复制代码
    type A = 'hello';
    
    // "HELLO"
    type B = Uppercase<A>;

    上面示例中,Uppercase<T>将 hello 转为 HELLO。

Lowercase<StringType>

Lowercase<StringType>将字符串的每个字符转为小写。

ini 复制代码
type A = 'HELLO';

// "hello"
type B = Lowercase<A>;

上面示例中,Lowercase<T>将 HELLO 转为 hello。

  1. Capitalize<StringType>

    Capitalize<StringType>将字符串的第一个字符转为大写。

    ini 复制代码
    type A = 'hello';
    
    // "Hello"
    type B = Capitalize<A>;

    上面示例中,Capitalize<T>将 hello 转为 Hello。

Uncapitalize<StringType>

Uncapitalize<StringType> 将字符串的第一个字符转为小写。

ini 复制代码
type A = 'HELLO';

// "hELLO"
type B = Uncapitalize<A>;

上面示例中,Uncapitalize<T>将 HELLO 转为 hELLO

注释指定

typescirpt 接受一些注释指令

采用 js 双斜杠注释的形式,向编译器发出命令

1. // @ts-nocheck

// @ts-nocheck告诉编译器不对当前脚本进行类型检查,可以用于 TypeScript 脚本,也可以用于 JavaScript 脚本。

ini 复制代码
// @ts-nocheck

const element = document.getElementById(123);

上面示例中,document.getElementById(123)存在类型错误,但是编译器不对该脚本进行类型检查,所以不会报错。

2. // @ts-check

如果一个 JavaScript 脚本顶部添加了// @ts-check,那么编译器将对该脚本 进行类型检查,不论是否启用了checkJs编译选项。

ini 复制代码
// @ts-check
let isChecked = true;

console.log(isChceked); // 报错

上面示例是一个 JavaScript 脚本,// @ts-check告诉 TypeScript 编译器对其进行类型检查,所以最后一行会报错,提示拼写错误。

3. // @ts-ignore

// @ts-ignore告诉编译器不对下一行代码进行类型检查,可以用于 TypeScript 脚本,也可以用于 JavaScript 脚本。

4.// @ts-expect-error

// @ts-expect-error主要用在测试用例,当下一行有类型错误时,它会压制 TypeScript 的报错信息(即不显示报错信息),把错误留给代码自己处理。

scss 复制代码
function doStuff(abc: string, xyz: string) {
  assert(typeof abc === "string");
  assert(typeof xyz === "string");
  // do some stuff
}

// @ts-expect-error
expect(() => {
  doStuff(123, 456);
}).toThrow();

上面示例是一个测试用例,倒数第二行的doStuff(123, 456)的参数类型与定义不一致,TypeScript 引擎会报错。但是,测试用例本身测试的就是这个错误,已经有专门的处理代码,所以这里可以使用// @ts-expect-error,不显示引擎的报错信息。

如果下一行没有类型错误,// @ts-expect-error则会显示一行提示。

arduino 复制代码
// @ts-expect-error
console.log(1 + 1);
// 输出 Unused '@ts-expect-error' directive.

上面示例中,第二行是正确代码,这时系统会给出一个提示,表示@ts-expect-error没有用到。

tsconfig.json

tsconfig.json 在根目录中定义ts 配置信息

执行tsc 命令 --init 参数自动生成tsconfig

csharp 复制代码
tsc --init

也可以 使用别人大神 预先写好的tsconfig.json 文件,npm 中@tsconfig 名称下有很多别人写好的tsconfig.json 样本,

例如 @tsconfig/recommended @tsconfig/node16

tsconfig.json的一级属性并不多,只有很少几个,但是compilerOptions属性有很多二级属性。下面先逐一介绍一级属性,然后再介绍compilerOptions的二级属性,按照首字母排序。

include

include属性指定所要编译的文件列表,既支持逐一列出文件,也支持通配符。文件位置相对于当前配置文件而定。

typescript 复制代码
{
    "include":['src/**/*',"test/**/*"]
}

如果不指定文件后缀名,默认包括.ts.tsx.d.ts文件。如果打开了allowJs,那么还包括.js.jsx

exclude

exclude属性是一个数组,必须与include属性一起使用,用来从编译列表中去除指定的文件 。它也支持使用与include属性相同的通配符。

extends

tsconfig.json 可以继承另一个 tsconfig.json 文件的配置 extends 属性也可以继承已发布的npm 模块里面的tsconfig 文件

typescript 复制代码
{
	"extends":"@tsconfig/node12/tsconfig.json"
}

自己定义的会覆盖extends 继承过来的配置

files

files 属性 指定编译的文件列表,如果有一个文件不存在就会报错

数组,排在前面的会先编译

json 复制代码
{
	"files":[a.ts,b.ts]
}

如果文件较多,建议使用include 和exclude

referneces

references属性可以用来定义项目之间的引用关系,并按照指定的顺序进行编译。这对于大型项目或包含多个子项目的项目结构非常有用。

下面是一个示例,说明如何在tsconfig.json中使用references属性:

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "outDir": "dist"
  },
  "references": [
    { "path": "./shared" },
    { "path": "./client" },
    { "path": "./server" }
  ]
}

在上面的示例中,有一个主项目,同时还有三个子项目:sharedclientserver。通过在references数组中添加对子项目的引用,我们可以告诉TypeScript编译器项目之间的依赖关系。

编译器会按照references数组中的顺序编译这些项目。在编译主项目之前,它会先编译依赖的子项目。这样确保了编译顺序的正确性,并且在编译过程中能够正确处理项目之间的依赖关系。

通过references属性,我们可以在大型项目中更好地管理和组织代码,提高编译效率,并减少潜在的编译错误。

compileOptions

该属性用来指定编译行为,如果该属性省略,这时编译器会使用默认设置

1.allowjs

当你在 TypeScript 项目中启用 "allowJs" 选项后,TypeScript 编译器将会接受并编译 JavaScript 文件,而不仅仅是 TypeScript 文件(.ts 文件)。这意味着你可以在 TypeScript 项目中直接引入和使用 JavaScript 模块。

使用 "allowJs" 选项的好处包括:

  • 平滑迁移:如果你正在进行从 JavaScript 到 TypeScript 的迁移,启用 "allowJs" 可以让你逐渐将 JavaScript 文件转换为 TypeScript 文件,而不需要一次性全部转换完毕。这样可以减少迁移的复杂度和风险。

  • 利用现有 JavaScript 生态系统:通过允许使用 JavaScript 文件,你可以直接使用来自 JavaScript 生态系统的现有库和工具,无需额外的转换或重新实现。

  • 扩展能力:TypeScript 中的类型检查和静态分析功能可以为 JavaScript 文件提供一些类型推断和错误检查的好处。这可以帮助你在使用 JavaScript 文件时捕获一些潜在的错误。

要启用 "allowJs" 选项,在 tsconfig.json 文件中的 "compilerOptions" 部分添加以下配置:

json 复制代码
{
  "compilerOptions": {
    "allowJs": true
  }
}

需要注意的是,尽管启用了 "allowJs" 选项,TypeScript 仍然会将 JavaScript 文件视为动态类型,并且不会对其进行类型检查。如果你需要享受 TypeScript 的完整类型检查功能,建议将 JavaScript 文件逐步转换为 TypeScript 文件。

baseUrl

指定ts 项目的基准目录 默认值为当前目录 ./

baseUrl为当前目录./。那么,当遇到下面的语句,TypeScript 将以./为起点,寻找hello/world.ts

javascript 复制代码
import { helloWorld } from "hello/world";
  1. checkJs 设置对js 文件进行检查,打开这个属性,相当于 自动设置allowJS:true,等于在js 脚本的头部添加 //@ts-check 命令

  2. jsx

    设置 jsx 如何处理 .txs 文件

    • preserve:保持 jsx 语法不变,输出的文件名为.jsx
    • react:将<div />编译成React.createElement("div"),输出的文件名为.js
    • react-native:保持 jsx 语法不变,输出的文件后缀名为.js
    • react-jsx:将<div />编译成_jsx("div"),输出的文件名为.js
    • react-jsxdev:跟react-jsx类似,但是为_jsx()加上更多的开发调试项,输出的文件名为.js
    json 复制代码
    {
      "compilerOptions": {
        "jsx": "preserve"
      }
    }
  3. lib

    编译时可访问的 JavaScript 标准库。

    lib值是一个数组,描述项目需要加载的 TypeScript 内置类型描述文件,跟三斜线指令/// <reference lib="" />作用相同。

  4. SourceMap 编译时是否生成 sourcemap 文件

  5. module module指定编译产物的模块格式。它的默认值与target属性有关,如果targetES3ES5,它的默认值是commonjs,否则就是ES6/ES2015

  6. target target指定编译出来的 JavaScript 代码的 ECMAScript 版本,比如es2021,默认是es3

该选项指定了编译后的 JavaScript 代码的目标版本。它决定了 TypeScript 编译器将生成哪个 ECMAScript(JavaScript)版本的代码。常用的选项包括:

  • "es5":将 TypeScript 编译为 ECMAScript 5 兼容的 JavaScript 代码。
  • "es6":将 TypeScript 编译为 ECMAScript 2015 兼容的 JavaScript 代码。
  • "es2017":将 TypeScript 编译为 ECMAScript 2017 兼容的 JavaScript 代码。
  • "esnext":将 TypeScript 编译为支持最新 ECMAScript 标准的 JavaScript 代码。
相关推荐
能来帮帮蒟蒻吗4 天前
VUE3 -综合实践(Mock+Axios+ElementPlus)
前端·javascript·vue.js·笔记·学习·ajax·typescript
菜鸟una4 天前
【taro3 + vue3 + webpack4】在微信小程序中的请求封装及使用
前端·vue.js·微信小程序·小程序·typescript·taro
struggle20255 天前
continue通过我们的开源 IDE 扩展和模型、规则、提示、文档和其他构建块中心,创建、共享和使用自定义 AI 代码助手
javascript·ide·python·typescript·开源
Bl_a_ck5 天前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
Bl_a_ck6 天前
开发环境(Development Environment)
开发语言·前端·javascript·typescript·ecmascript
菜鸟una6 天前
【layout组件 与 路由镶嵌】vue3 后台管理系统
前端·vue.js·elementui·typescript
浪裡遊7 天前
Typescript中的对象类型
开发语言·前端·javascript·vue.js·typescript·ecmascript
从味书7 天前
安装typescript时,npm install -g typescript报错
javascript·typescript·npm
風吹过9 天前
A* (AStar) 寻路
typescript·cocos2d
geovindu9 天前
vue3: pdf.js 2.16.105 using typescript
javascript·vue.js·typescript·pdf