TS 基础

摘要 :本文系统梳理 TypeScript 中几类进阶类型技巧:先介绍 npx 工具链的用法,再讲解命名空间与函数/类的合并特性;随后深入类型断言(as const、satisfies)与自定义类型守卫(is);接着进入类型编程,涵盖条件类型与 infer 提取、映射类型修饰符、模板字面量类型及 this 类型声明;最后介绍泛型的约束、默认类型与分布式条件类型。全文以可运行的代码示例为主,帮助读者快速掌握这些提升类型安全与开发效率的实用能力。

TS 文件编译

npx = Node Package eXecute,是 npm 自带(npm 5.2+ 起内置)的命令行工具,用于执行 npm 包里的可执行文件。

  • npx tsc xxx.ts 临时使用,无需全局安装;

  • tsx xxx.ts 需要全局安装(npm install -g tsx)。

    npx tsc xxx.ts --outDir dist # 编译 node dist/xxx.js # 需要安装 npm install typescript
    npx tsx xxx.ts # 用 esbuild 即时转译 + 运行 //开发环境经常使用
    npx ts-node xxx.ts # 用 tsc 类型检查 + 即时编译
    npx tsc --noEmit # 要类型检查时使用

1. 命名空间与函数/类合并

TypeScript 允许同名的接口或命名空间自动合并,这是它独有的特性。

复制代码
function Counter() {}
namespace Counter {
  export let count = 0;
  export function increment() { count++; }
}
Counter()
Counter.increment();
console.log(Counter.count); // 有类型提示

2. 类型断言与修饰符

2.1. as const 常量断言

将对象或数组变为完全只读,并将类型收窄为最具体的字面量类型。

复制代码
let arr = [1, 2, 3] as const; // 类型变为 readonly [1, 2, 3],不能 push

let obj = { name: 'Tom', age: 18 } as const;
// 类型变为 { readonly name: "Tom"; readonly age: 18; }

2.2 satisfies 操作符(TS 4.9+)

确保变量满足某个类型,同时不丢失原始类型的具体信息(这是与 as 断言最大的区别)。

复制代码
type Colors = 'red' | 'blue';
type ColorMap = Record<Colors, string>;

// 使用 as 会丢失具体 key 的信息
const badMap = { red: '#f00', blue: '#00f', green: '#0f0' } as ColorMap; // 不报错 但 key被收窄 
// 使用 satisfies 既检查类型,又保留具体字面量
const goodMap = { red: '#f00', blue: '#00f', green: '#0f0' } satisfies ColorMap;// ❌ 报错,对象字面量只能指定已知属性,并且"green"不在类型"ColorMap"中。
// goodMap.green 仍然可以访问,且类型推断为 string 

2.3 自定义 类型守卫(is 关键字)

复制代码
/**
 * `val is string` 是 TypeScript 的 类型谓词语法 。
 * 它告诉编译器:
 *  - 当`isString(val)` 返回`true` 时,`val` 的类型会被收窄为`string` 。
 * @param val 要检查的值
 * @returns 如果值是字符串则返回 true,否则返回 false
 */
function isString(val: any): val is string {
    return typeof val === 'string';
}

function printLength(val: string | number) {
    if (isString(val)) {
        console.log(val.length); // TS 知道这里 val 是 string
    }
}

单纯typeof val === 'string' 也能在 TS 中触发类型收窄。isString 这类封装的价值在于:

  • 作为 可复用函数 在多处调用,避免重复写typeof

  • 可以组合更复杂的判断(如isString + 非空 + 符合某种格式)

    function isNonEmptyString ( val : any ): val is string {
    return typeof val === 'string' && val. length > 0 ;
    }

    /**

    • 编译器是不会怀疑的
    • 你说是就是吧
    • 你可以调用字符串的方法
      */
      function isErrorString(val: any): val is string {
      return typeof val === 'number';
      }

3. 类型编程

3.1 条件类型与 infer 提取

在类型声明中使用三元运算,并通过 infer 推断内部类型,这里有个词语 "类型可分配性检查" (assignability check),意思是:T extends Promise<infer U> T 是否赋值给 Promise<infer U>结构类型。

注意:extends 在这里的作用是 模式匹配 + 类型提取

复制代码
// 提取 Promise 里的返回值类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;


type Result = UnwrapPromise<Promise<string>>; // string

//结构兼容 Promise(有 then 方法),即使没"继承"Promise
interface Thenable {
  then<U>( onfulfilled : ( v : any ) => U): Promise <U>;
} 
type C = UnwrapPromise < number >; // number(走 false 分支)

3.2 映射类型与修饰符(+ / -)

在映射类型中,可以添加或移除​ readonly 和 optional 修饰符

复制代码
type Mutable<T> = {
    -readonly [K in keyof T]: T[K]; // 移除 readonly
};

type RequiredFields<T> = {
    [K in keyof T]-?: T[K]; // 移除 ? (变为必选)
};

type ReadonlyPartial<T> = {
    +readonly [K in keyof T]+?: T[K]; // 添加 readonly 和 ? (默认就是加,+可省略)
};

// 示例
const user: ReadonlyPartial<{
    name: string;
    age: number;
}> = {
    name: '张三',
    age: 18, 
}; 
user.age = 20; //  报错,因为 age 是 readonly 类型

3.3 模板字面量类型(Template Literal Types)

像写 JS 模板字符串一样声明类型

复制代码
type HttpMethod = 'get' | 'post' | 'put' | 'delete';
type ApiEndpoint = `/api/${HttpMethod}/${string}`;

const url: ApiEndpoint = '/api/get/users'; // 正确
const badUrl: ApiEndpoint = '/api/head/users'; //类型报错

3.4 this 类型声明

函数参数列表中第一个位置声明 this 的类型(它不会生成实际 JS 参数)

复制代码
function greet(this: { name: string }, msg: string) {
    console.log(msg);
    return `${this.name}: ${msg}`;
}

const obj = { name: 'Tom', greet };
obj.greet('Hello'); //  this 指向 obj
//greet('Hello'); //  直接调用会报错,因为 this 不是预期类型

方案2

复制代码
interface MyObject {
    name: string;
    logName(): void;
}

const obj2: MyObject & ThisType<MyObject> = {
    name: '李四',
    logName() {
        console.log(this.name); // this 被推断为 { name: string }
    }
}; 
obj2.logName(); //  this 指向 obj2 

4 泛型

4.1 泛型约束与默认类型

复制代码
// T 必须继承 object,且默认是空对象
type Config<T extends object = {}> = {
  data: T;
  timestamp: number;
};

4.2 分布式条件类型

当泛型参数是联合类型时,条件类型会自动分发

复制代码
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>; // string[] | number[]
相关推荐
aloha_7891 天前
HTML、JavaScript 与 TypeScript 基础速查
javascript·typescript·html
离歌漠2 天前
《我用 Tauri 做了一个本地开发工具箱》
rust·typescript·vue
enjoy编程2 天前
playwright学习-I
typescript·vue·nodejs·1024程序员节·playwright
iDao技术魔方2 天前
Bun v1.4.4 实战:用 bun fmt 替掉 prettier 的 5 个步骤
typescript·1024程序员节
梅雅达编程笔记2 天前
Day 17 | uni-app 小程序:前端从零搭建
微信小程序·typescript·uni-app·vue3·pinia·前端开发·全栈
康小旺2 天前
TypeScript 编译器开始卷 Rust 了:Cloudflare 的 tsrs 到底想干嘛?
rust·typescript
nyaomaru3 天前
is-kit 达成 100 Stars ⭐🎉 从 50 到 100,发生了什么?
前端·typescript
ZoeLandia4 天前
React6:组件通信
前端·react.js·typescript
孟陬4 天前
解决动态 import 类型 Any 痼癖
typescript·node.js·bun
ZoeLandia4 天前
React03:useState 与不可变更新
前端·react.js·typescript·react