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[]
相关推荐
光影少年6 小时前
Taro 是如何解析入口配置 app.config.ts 和页面配置的?
微信小程序·小程序·typescript·reactjs·百度小程序·taro
AI情绪识别开源1 天前
HFpEF 心房-心室耦联效率无创监测方向(AVCEI)
typescript·laravel·perl
凤城老人1 天前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
zhousenshan2 天前
TypeScript 入门到精通
typescript
花旗蜕变3 天前
前端转 NestJS 全栈实践:从表单页面到微信业务系统
前端·typescript·nestjs
艺杯羹3 天前
Anthropic突袭发布Claude Code Mods!用TypeScript编写智能体中间件:告别AI误删与失控
人工智能·typescript·系统架构
FYKJ_20105 天前
SSM校园失物招领系统41452-计算机课程设计、毕业设计
java·vue.js·spring boot·python·mysql·typescript·spark
tiankongdeyige6 天前
游戏战斗系统学习第2篇:有限状态机 (FSM)
学习·游戏·typescript
FYKJ_20106 天前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
进击的明明6 天前
TypeScript速通笔记(下)
前端·面试·typescript