都2025了,你确定你完全掌握Typescript了吗

前言

2012年10月微软发布了Typescript第一个开源的版本,那时候作为一个前端开发者可能只是知道它,随着近几年前端工程化的发展,加上Vue这类框架也开始全面拥抱Typescript,才让越来越多的前端开发者开始使用它。但是反观使用了Typescript的开发者,对这个语言知之甚少,一部分还停留在使用interfacetype定义类型的阶段。

类型兼容性

Typescript类型系统是按照结构子类型设计的,它基于类型的成员或属性来决定类型之间的关系,而不是基于类型的显式声明或继承关系。

对象类型兼容性(协变)

typescript 复制代码
interface Animal {
  name: string;
}

interface Dog  {
  name: string;
  color: string;
}

let animal: Animal = {
  name: 'animal',
}

let dog: Dog = {
  name: 'dog',
  color: 'white',
}

animal = dog; // Ok 可以赋值

dog = animal; // Error 不可以赋值

在这个例子中,Dog同时包含namecolor属性,包含了Animal所有属性,所以在typescriptDog被认为是Animal的子类型,在赋值的时候dog可以赋值给animal,但是反过来却不行,这主要是typescript出于类型安全考虑,比如dog赋值给animal,在后面使用animal的时候能够保证所有的成员都是可用的,但是反过来赋值的话,却不能保证dog的每个成员都是可用的。

函数参数兼容性(逆变)

typescript 复制代码
interface Animal {
    name: string;
}

interface Dog  {
    name: string;
    color: string;
}

type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;

let handleDog: DogHandler = (dog) => {};
let handleAnimal: AnimalHandler = (animal) => {};

handleDog = handleAnimal;  // Ok 可以赋值

handleAnimal = handleDog;  // Error 不可以赋值

这个例子可以用上面的方法来分析,把handleAnimal赋值给handleDog可以,后面调用handleDog的时候实际调用的是handleAnimal函数,传入Dog类型的参数是可以保证正常工作的,因为Dog包含了handleAnimal需要的Animal参数的所有成员。反过来把handleDog赋值给handleAnimal就是不可以的

一些操作符

is

is 是 TypeScript 中的一种类型谓词(Type Predicate),用于自定义类型保护(Custom Type Guard)。它允许你编写函数来显式地告诉 TypeScript 编译器某个值的具体类型。

  • 基本语法
typescript 复制代码
function isType(value: any): value is TargetType {
  // 返回布尔值
  // 如果返回 true,TypeScript 会认为 value 是 TargetType 类型
}
  • 举例
    比如Vue3的isRef函数,用来判断一个值是不是ref对象
typescript 复制代码
// 类型定义
function isRef<T>(r: Ref<T> | unknown): r is Ref<T>

// 使用
let foo: unknown
if (isRef(foo)) {
  // foo的类型被收缩为 Ref<unknown>
  foo.value
}

typeof

typeof 在 TypeScript 中有两种主要用法,分别用于值上下文类型上下文

  • 值上下文中的 typeof (JavaScript 行为)
    在表达式/值层面使用,与 JavaScript 相同,返回值的类型字符串:
typescript 复制代码
const str = "hello";
const num = 42;

console.log(typeof str); // "string"
console.log(typeof num); // "number"

function greet() {}
console.log(typeof greet); // "function"
  • 类型上下文中的 typeof (TypeScript 扩展)
    在类型位置使用,可以获取变量或属性的类型:
typescript 复制代码
const person = {
  name: "Alice",
  age: 30
};

// 获取 person 的类型
type Person = typeof person;
/* 等价于:
type Person = {
  name: string;
  age: number;
}
*/

// 获取特定属性的类型
type Age = typeof person["age"]; // number

keyof

keyof 是 TypeScript 中的一个类型操作符,用于获取一个类型的所有属性名(键)组成的联合类型。

  • 用法
typescript 复制代码
interface Person {
  name: string;
  age: number;
  address: string;
}

type PersonKeys = keyof Person;
// 等价于: "name" | "age" | "address"

in

in 在 TypeScript 中有两种主要用法,分别用于值上下文类型上下文

  • 运行时上下文(JavaScript 行为)
    在 JavaScript/TypeScript 运行时逻辑中,in 用于检查对象是否包含特定属性:
typescript 复制代码
const user = { name: "Alice", age: 30 };

// 检查属性是否存在
console.log("name" in user); // true
console.log("email" in user); // false

// 也可用于检查原型链上的属性
console.log("toString" in user); // true
  • 类型上下文(TypeScript 类型系统)
    在类型系统中,in 主要有两种用途:
    (1) 类型保护
    缩小联合类型的范围:
typescript 复制代码
interface Bird {
  fly(): void;
}

interface Fish {
  swim(): void;
}

function move(pet: Bird | Fish) {
  if ("fly" in pet) {
    pet.fly(); // 这里 pet 被识别为 Bird
  } else {
    pet.swim(); // 这里 pet 被识别为 Fish
  }
}

(2) 映射类型

用于遍历联合类型的每个成员:

typescript 复制代码
type FeatureFlags = {
  darkMode: boolean;
  newProfile: boolean;
};

// 将每个属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};
type Options = Partial<FeatureFlags>;

/*  等价于:
type Options = {
  darkMode?: boolean;
  newProfile?: boolean;
};
*/

extends

extends 是 TypeScript 中一个多功能的关键字,在不同的上下文中有不同的作用。

  • 接口/类继承 (面向对象继承)
    用于接口或类之间的继承关系:
typescript 复制代码
// 接口继承
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// 类继承
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  breed: string;
  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }
}
  • 类型约束
    限制泛型参数的类型范围:
typescript 复制代码
// 要求 T 必须具有 length 属性
function logLength<T extends { length: number }>(arg: T): void {
  console.log(arg.length);
}

logLength("hello"); // 5
logLength([1, 2, 3]); // 3
logLength(42); // 错误,数字没有length属性
  • 类型推断 与 infer 关键字配合使用,在条件类型中提取类型信息
typescript 复制代码
function move(distance: number) {
  return distance;
}
// typescript内置工具ReturnType
// 获取函数的返回值类型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
type MoveReturn = ReturnType<typeof move>;

/* 等价于
type MoveReturn = number
*/

类型查找规则

模块类型查找规则

  • 非相对导入(如 import {cloneDeep} from 'lodash-es')
    1. 查找node_modules/@types/lodash-es/index.d.ts
    2. 根据模块自身的package.json中定义的types字段,查找指定的文件
    3. 查找node_modules/lodash-es/index.d.ts
    4. 查找 typeRoots 指定的目录(如果配置了)
  • 相对导入(如 import { Foo } from './foo'
    1. 查找同目录下的文件, ./foo.ts./foo.tsx./foo.d.ts./foo/index.ts./foo/index.d.ts

全局类型查找规则

  • Typescript会根据指定的target加载内置的类型声明文件

    1. target: "ES5" → 默认包含 DOM, ES5, ScriptHost
    2. target: "ES6" → 默认包含 DOM, ES6, DOM.Iterable, ScriptHost
      这些内置声明文件位于 TypeScript 语言安装目录的lib文件夹内,数量大概有几十个,下面是其中一些主要文件
    • lib.d.ts
    • lib.dom.d.ts
    • lib.es2015.d.ts
    • lib.es2016.d.ts
    • lib.es2017.d.ts
    • lib.es2018.d.ts
    • lib.es2019.d.ts
    • lib.es2020.d.ts
    • lib.es5.d.ts
    • lib.es6.d.ts
  • 默认会包含所有.ts.d.ts.tsx文件
    如果没有指定filesinclude,编译器默认包含当前目录和子目录下所有的TypeScript文件(.ts, .d.ts.tsx),这些文件中定义的全局类型会在全局可用

typescript 复制代码
// global.d.ts
// 因为这个文件没有明确的export或import,所以文件定义就是全局类型
interface Window {
  a: number;
  b: number;
  c: number;
}

declare let d: number;

// 如果有了export导出,可以使用global声明全局类型, 下面写法和上面写法是等价的
declare global {
  interface Window {
    a: number;
    b: number;
    c: number;
  }
  let d: number;
}
export {};

类型扩展

有时候开发过程中,不免会遇到一些情况需要扩展现在既有的第三方库的类型

  • 扩展第三方库vue-router的meta属性
typescript 复制代码
// global.d.ts 或 router.ts
import 'vue-router';

declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean
  }
}
  • 扩展全局对象window
typescript 复制代码
// global.d.ts
export {};
declare global {
  interface Window {
    handleJump: () => void;
  }
}

typescript文件的执行和编译工具

  • 执行typescript文件的工具有ts-nodetsx
  • 做typescript文件的转义的工具有Typescript自带的tscesbuild
  • 做typescript文件类型检查的工具有Typescript自带的tsctsserver
相关推荐
旭久10 分钟前
react+antd封装一个可回车自定义option的select并且与某些内容相互禁用
前端·javascript·react.js
是纽扣也是烤奶15 分钟前
关于React Redux
前端
阿丽塔~17 分钟前
React 函数组件间怎么进行通信?
前端·javascript·react.js
冴羽41 分钟前
SvelteKit 最新中文文档教程(17)—— 仅服务端模块和快照
前端·javascript·svelte
uhakadotcom43 分钟前
Langflow:打造AI应用的强大工具
前端·面试·github
前端小张同学1 小时前
AI编程-cursor无限使用, 还有谁不会🎁🎁🎁??
前端·cursor
yanxy5121 小时前
【TS学习】(15)分布式条件特性
前端·学习·typescript
uhakadotcom1 小时前
Caddy Web服务器初体验:简洁高效的现代选择
前端·面试·github
前端菜鸟来报道1 小时前
前端react 实现分段进度条
前端·javascript·react.js·进度条