TS 基础

摘要 :本文系统梳理 TypeScript 中几类进阶类型技巧:先介绍 npx 工具链的用法,再讲解命名空间与函数/类的合并特性;随后深入类型断言(as constsatisfies)与自定义类型守卫(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[]
相关推荐
西瓜太郎4991 小时前
别再手算 Token 成本:用 React 状态机做一个可追溯的模型费用计算器
react.js·typescript
右耳朵猫AI6 小时前
Web前端周刊2026W37 | Shopify 转原生、React 编译 Rust 化、Vitest 5.0、Rslib 1.0
前端·javascript·react.js·typescript·node.js
晚安日记wanna1 天前
TS const 类型参数一道面试题的四层追问
前端·面试·typescript
掰头战士1 天前
你说你折这玩意干什么-agent工作的核心,还真得好好考虑怎么折叠上下文
typescript·llm·agent
ynchyong1 天前
VUE 中 不能将类型“R[]”分配给类型“UnwrapRefSimple<R>[]”
typescript·vue·ts·unwraprefsimple
掰头战士1 天前
让散乱的工具调用成为正规军,今天咱们聊聊 Tool System管线的读写锁
typescript·llm·agent
半糖程序员1 天前
从零构建 Agent(5):让事件流同时提供过程和结果
typescript·agent