都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
相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax