尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TypeScript `unknown` 类型深度指南:如何用 type-safe 方案替代 `any`(The Concise TypeScript Book 实战解析)

TypeScript `unknown` 类型深度指南:如何用 type-safe 方案替代 `any`(The Concise TypeScript Book 实战解析) 文档教程【免费下载链接】typescript-bookThe Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.项目地址https://gitcode.com/gh_mirrors/typ/typescript-book点击查看免费下载unknown是 TypeScript 中专门表示类型未知值的类型它允许你安全地接收来自外部 API、用户输入或第三方库的任意数据同时强制你在真正使用这些值之前完成类型检查type check或类型断言type assertion。读完本文你将掌握unknown与any的本质区别、它的赋值assignability规则、配合类型收窄narrowing的正确使用方式以及在真实项目中用unknown加固代码安全性的完整实战方案。本文内容以 The Concise TypeScript Book 开源仓库的 Unknown type 章节及其意大利语翻译版 unknown-type.md为骨架展开。什么是unknown类型在 TypeScript 中unknown类型表示一个类型未知的值a value that is of an unknown type。它最核心的设计意图是你可以把一个不确定来源的值声明为unknown但在对它执行任何具体操作之前编译器会强制你先完成类型检查或类型收窄将其限定到一个更具体的类型上。与any类型不同——any允许对值做任意操作而不会报错——unknown不允许在没有先断言assert或收窄narrow到更具体类型的情况下进行任何操作。这正是它被称为 type-safe alternative toanyany的类型安全替代品的根本原因。从仓库结构看该章节位于本书 Primitive Types / Built-in Types 体系中的第 46 节sidebar order: 46紧随第 45 节 Tipo Any英文版 any-type.md之后二者在目录设计上就构成了宽松任意类型 vs 安全未知类型的对照关系。unknown与any的核心区别对比两个类型的章节定义可以清晰看到设计定位的差异维度anyany-type.mdunknownunknown-type.md含义通用超类型universal supertype可表示任何类型的值基本类型、对象、数组、函数、错误、symbol表示类型未知的值操作限制无任何限制编译器放行所有操作使用前必须先做类型检查、断言或收窄类型安全会削弱类型安全容易让错误静默扩散是any的类型安全替代品赋值能力可赋给任意类型只能赋给any和unknown本身适用场景编译期类型未知、外部无类型定义的 API/库需谨慎限量使用需要接收未知数据、同时保留类型安全检查的场景原文档还特别给出了使用any的纪律建议见于 any-type.md这些建议反向印证了unknown的设计动机将any的使用限制在类型确实未知的特定场景不要让函数返回any否则会削弱调用方代码的类型安全如果只是想屏蔽编译器报错优先用ts-ignore而不是any。当你在编写代码时发现不得不引入any第一反应应该换成unknown 类型收窄这正是本书把unknown章节紧排在any之后的教学意图。unknown的赋值规则assignability原文档明确给出了unknown类型唯一的核心规则unknown只能赋值给any和unknown自身不能赋值给任何其他具体类型。原文档中的完整示例含中文注释let value: unknown; let value1: unknown value; // Valido —— 合法unknown 可赋给 unknown let value2: any value; // Valido —— 合法unknown 可赋给 any let value3: boolean value; // Non valido —— 非法unknown 不能直接赋给 boolean let value4: number value; // Non valido —— 非法unknown 不能直接赋给 number这一规则的含义非常实际从unknown到具体类型的下行赋值被严格禁止。当你把某个值声明为unknown后必须先通过类型收窄narrowing或类型断言将其洗白成具体类型才能赋给boolean、number、string等目标类型。反过来任意类型的值都可以赋给unknown上行方向不受限因为任意值在编译期看来都可能是未知类型是安全的方向。相关知识点赋值收窄Assignments narrowing虽然unknown本身不能直接赋给具体类型但一旦你在代码中把某个联合类型的变量赋上具体值TypeScript 会基于赋值语句自动收窄其类型见本书第 22 节 assignments.mdlet value: string | number; value hello; if (typeof value string) { console.log(value.toUpperCase()); // 此处 value 被收窄为 string } value 42; if (typeof value number) { console.log(value.toFixed(2)); // 此处 value 被收窄为 number }这套赋值 → 收窄 → 安全使用的机制与unknown的检查 → 收窄 → 安全使用在使用心智上是完全一致的TypeScript 只允许你在类型信息被确认之后执行对应操作。对unknown使用类型守卫typeof type guard既然unknown不允许直接操作那如何安全地使用它原文档给出了最典型的实战模式——用typeof类型守卫收窄后再运算const add (a: unknown, b: unknown): number | undefined typeof a number typeof b number ? a b : undefined; console.log(add(1, 2)); // 3 console.log(add(x, 2)); // non definitoundefined这段代码展示了unknown的完整安全使用闭环函数的两个入参被声明为unknown因此无论调用方传入数字、字符串还是其他任意值编译期都不报错在运算前用typeof a number typeof b number做双重检查只有两个参数都被确认为number时才会执行a b算术运算并返回number任何一方不是数字时走undefined分支返回值类型声明为number | undefined。注意没有typeof检查时直接写a b是编译错误因为unknown上不允许任何操作——这正是unknown相对于any的保护价值所在。typeof检查在运行时也会真实执行所以这段代码在 JavaScript 层面同样安全不会出现字符串与数字相加这类隐式转换的脏数据。更多收窄手段源于本书第 21 节 narrowing.mdunknown收窄并不局限于typeof本书 narrowing.md 列出了 TypeScript 官方支持的全部收窄手段均可配合unknown使用typeof 类型守卫基于 JavaScript 内置类型number、string、boolean、object、function、undefined、symbol、bigint检查来收窄真值性收窄truthiness narrowing通过判断变量是否为 truthy/falsy 来排除null、undefined、0、等分支相等性收窄equality narrowing结合switch与、!、、!运算符按具体值收窄in运算符收窄依据对象是否拥有某属性如breed in pet来区分对象类型instanceof收窄依据构造函数实例关系收窄到具体类。例如用instanceof处理类实例型的unknownclass Square { constructor(public width: number) {} } class Rectangle { constructor(public width: number, public height: number) {} } function area(shape: unknown) { if (shape instanceof Square) { return shape.width * shape.width; } else if (shape instanceof Rectangle) { return shape.width * shape.height; } return undefined; }控制流分析对收窄的影响源于本书第 23 节 control-flow-analysis.mdunknown的收窄依赖 TypeScript 的静态控制流分析Control Flow Analysis。本书 control-flow-analysis.md 指出自 TypeScript 4.4 起控制流分析不仅作用于if语句块内还能作用于条件表达式以及通过const变量间接引用的判别属性const f1 (x: unknown) { const isString typeof x string; if (isString) { x.length; // OK通过 const 变量 isString 间接收窄了 x } };同时文档也明确列出了不会发生收窄的反例值得在实战中警惕const f1 (x: unknown) { let isString typeof x string; // 用 let 声明不是 const if (isString) { x.length; // ErrorisString 不是 const无法间接收窄 x } }; const f6 (obj: { kind: foo; foo: string } | { kind: bar; bar: number }) { const isFoo obj.kind foo; obj obj; // 函数体内对 obj 发生了重新赋值 if (isFoo) { obj.foo; // Errorobj 在函数体内被赋值收窄信息失效 } };此外条件表达式中的间接分析最多支持五层间接引用Up to five levels of indirection。这意味着当你用unknown接收数据时请尽量把类型检查结果保存在const变量中、避免在检查后重新赋值原变量否则收窄会静默失效操作会重新报错。类型谓词Type Predicates自定义收窄函数源于本书第 24 节当typeof、instanceof等内置收窄手段不足以描述你的业务判断时可以用类型谓词函数自定义收窄逻辑它特别适合处理unknown入参见 type-predicates.mdconst isString (value: unknown): value is string typeof value string; const foo (bar: unknown) { if (isString(bar)) { console.log(bar.toUpperCase()); // bar 被收窄为 string } else { console.log(not a string); } };同一章节还提到 TypeScript 5.5 起会自动推断类型谓词如x is T因此.filter(x x ! null)这类清晰检查可以自动移除null得到更精确的类型但!!x这类模糊检查不会触发推断。实战用unknown安全处理外部数据把上述知识组合起来最常见的落地场景是解析外部不可信数据API 响应、JSON 解析、表单输入。以经典的JSON.parse为例// 推荐先以 unknown 接收再逐步收窄 function parseJson(input: string): unknown { return JSON.parse(input); } const data: unknown parseJson({name:TypeScript,version:5.5}); if ( typeof data object data ! null name in data typeof (data as { name: unknown }).name string ) { const name: string (data as { name: string }).name; // 安全提取 console.log(name.toUpperCase()); }这里遵循了unknown的完整纪律先确认是对象、排除null因为typeof null object、再用in运算符确认属性存在、最后用typeof确认属性值的具体类型每一步都在编译器监督下进行。你也可以把这段判断抽取成类型谓词函数参考上面的isString让收窄逻辑可复用。结合原文档的add示例一个完整的防御式工具函数可以这样写const toNumber (value: unknown): number | undefined typeof value number ? value : undefined; const addSafe (a: unknown, b: unknown): number | undefined { const na toNumber(a); const nb toNumber(b); return na ! undefined nb ! undefined ? na nb : undefined; };最佳实践小结综合原文档与仓库中 any-type.md、narrowing.md、type-predicates.md、control-flow-analysis.md 各章节使用unknown时可以遵循以下准则优先用unknown替代any需要表达类型未知时unknown是安全默认项any只保留给确有必要的少量场景使用时必须收窄typeof、instanceof、in、真值性、相等性收窄以及自定义类型谓词都是unknown的合法解锁方式把收窄结果保存在const中避免let与函数体内重新赋值防止控制流分析失效不要让unknown泄漏回any或具体类型未经检查就断言如value as string会绕过编译期保护应仅在收窄逻辑完备时使用函数边界处隔离在模块/函数边界用unknown接收外部数据在内部完成校验后输出强类型结果类似parseJson → 校验 → 强类型 DTO的模式让不安全范围最小化。延伸阅读与unknown对照的类型Tipo Any / any-type.md类型收窄全集narrowing.md控制流分析与收窄限制control-flow-analysis.md自定义收窄与 TS 5.5 谓词推断type-predicates.md赋值触发的收窄assignments.md从不发生的类型never常与unknown搭配做穷尽检查never-type.md赞分享文档教程【免费下载链接】typescript-bookThe Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.项目地址https://gitcode.com/gh_mirrors/typ/typescript-book点击查看免费下载相关推荐The Concise TypeScript Book 深度解析unknown 类型 —— 类型安全的 any 替代方案The Concise TypeScript Book 深度解析 unknown 类型 —— 类型安全的 any 替代方案 unknown 是 TypeScr文档教程The Concise TypeScript Book深入理解 unknown 类型——any 的类型安全替代方案The Concise TypeScript Book深入理解 unknown 类型——any 的类型安全替代方案 《The Concise TypeScri文档教程TypeScript 的 any 类型全解通用超类型本质、noImplicitAny 防线与 unknown 替代方案The Concise TypeScript Book 实战指南TypeScript 的 any 类型全解通用超类型本质、noImplicitAny 防线与 unknown 替代方案The Concise TypeScr文档教程上一篇oneTBB 任务调度器Task Scheduler完全指南任务组、任务竞技场与调度控制在 mold 链接器中的实战应用下一篇Data-Science-For-Beginners 课程 10用直方图与密度图可视化数据分布Matplotlib Seaborn 实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表