typescript-mastery
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Mastery
TypeScript 精通指南
<!-- dual-compat-start -->
<!-- dual-compat-start -->
Use When
适用场景
- Comprehensive TypeScript skill covering the full type system: fundamentals, generics, conditional/mapped/template literal types, utility types, strict mode, React patterns, production tsconfig, and advanced patterns from Boris Cherny (variance...
- The task needs reusable judgment, domain constraints, or a proven workflow rather than ad hoc advice.
- 全面的TypeSkill技能,涵盖完整类型系统:基础知识、泛型、条件/映射/模板字面量类型、工具类型、严格模式、React模式、生产环境tsconfig,以及Boris Cherny提出的高级模式(方差...
- 任务需要可复用的判断逻辑、领域约束或成熟的工作流,而非临时建议。
Do Not Use When
不适用场景
- The task is unrelated to or would be better handled by a more specific companion skill.
typescript-mastery - The request only needs a trivial answer and none of this skill's constraints or references materially help.
- 任务与无关,或更适合由更专业的配套技能处理。
typescript-mastery - 请求仅需要简单答案,本技能的约束或参考内容无法提供实质性帮助。
Required Inputs
必需输入
- Gather relevant project context, constraints, and the concrete problem to solve.
- Confirm the desired deliverable: design, code, review, migration plan, audit, or documentation.
- 收集相关项目背景、约束条件以及具体需要解决的问题。
- 确认期望交付物:设计方案、代码、评审意见、迁移计划、审计报告或文档。
Workflow
工作流
- Read this first, then load only the referenced deep-dive files that are necessary for the task.
SKILL.md - Apply the ordered guidance, checklists, and decision rules in this skill instead of cherry-picking isolated snippets.
- Produce the deliverable with assumptions, risks, and follow-up work made explicit when they matter.
- 先阅读本,然后仅加载任务所需的相关深度文档。
SKILL.md - 应用本技能中的有序指导、检查清单和决策规则,而非零散选取片段。
- 生成交付物时,若相关需明确说明假设、风险和后续工作。
Quality Standards
质量标准
- Keep outputs execution-oriented, concise, and aligned with the repository's baseline engineering standards.
- Preserve compatibility with existing project conventions unless the skill explicitly requires a stronger standard.
- Prefer deterministic, reviewable steps over vague advice or tool-specific magic.
- 输出内容需以执行为导向,简洁明了,并与仓库的基准工程标准保持一致。
- 除非技能明确要求更高标准,否则需保持与现有项目约定的兼容性。
- 优先采用确定性、可评审的步骤,而非模糊建议或工具特定的“魔法操作”。
Anti-Patterns
反模式
- Treating examples as copy-paste truth without checking fit, constraints, or failure modes.
- Loading every reference file by default instead of using progressive disclosure.
- 将示例视为可直接复制粘贴的真理,而不检查是否适配、约束条件或失败模式。
- 默认加载所有参考文件,而非逐步按需披露。
Outputs
输出成果
- A concrete result that fits the task: implementation guidance, review findings, architecture decisions, templates, or generated artifacts.
- Clear assumptions, tradeoffs, or unresolved gaps when the task cannot be completed from available context alone.
- References used, companion skills, or follow-up actions when they materially improve execution.
- 符合任务需求的具体结果:实现指导、评审发现、架构决策、模板或生成的工件。
- 当仅靠现有上下文无法完成任务时,需明确说明假设、权衡或未解决的空白。
- 当能实质性提升执行效果时,需列出使用的参考资料、配套技能或后续行动。
Evidence Produced
生成的证据
| Category | Artifact | Format | Example |
|---|---|---|---|
| Correctness | Type-system test plan | Markdown doc covering generic, conditional, mapped, and template-literal type tests | |
| 类别 | 工件 | 格式 | 示例 |
|---|---|---|---|
| 正确性 | 类型系统测试计划 | 涵盖泛型、条件、映射和模板字面量类型测试的Markdown文档 | |
References
参考资料
- Use the links and companion skills already referenced in this file when deeper context is needed.
Production-grade TypeScript. Synthesised from Total TypeScript (Pocock), Ultimate TypeScript Handbook (Wellman), 250 Killer TypeScript One-Liners (Abella), and Programming TypeScript (Cherny).
- 当需要更深入的上下文时,使用本文件中已引用的链接和配套技能。
生产级TypeScript。综合自《Total TypeScript》(Pocock)、《Ultimate TypeScript Handbook》(Wellman)、《250 Killer TypeScript One-Liners》(Abella)以及《Programming TypeScript》(Cherny)。
1. Fundamentals
1. 基础知识
Basic Types and Inference
基础类型与类型推断
typescript
let name: string = "Alice";
let id: string | number; // union
let val: unknown; // safe unknown — must narrow before use
let never_: never; // unreachable / exhaustive check
const ids: string[] = [];
const entry: [string, number] = ["Alice", 30]; // tuple
const named: [name: string, age: number] = ["Alice", 30]; // named tuple
const greet = (name: string, age = 30): string => `Hello ${name}`;
const concat = (first: string, last?: string) => last ? `${first} ${last}` : first;typescript
let name: string = "Alice";
let id: string | number; // union
let val: unknown; // safe unknown — must narrow before use
let never_: never; // unreachable / exhaustive check
const ids: string[] = [];
const entry: [string, number] = ["Alice", 30]; // tuple
const named: [name: string, age: number] = ["Alice", 30]; // named tuple
const greet = (name: string, age = 30): string => `Hello ${name}`;
const concat = (first: string, last?: string) => last ? `${first} ${last}` : first;Type Aliases vs Interfaces
类型别名 vs 接口
typescript
type ID = string | number; // unions, primitives → type
type Status = "active" | "inactive"; // literal unions → type
interface User { // object shapes → interface (merges)
id: string;
name: string;
email?: string;
}
interface AdminUser extends User { roles: string[] }
type AdminUser = User & { roles: string[] }; // intersection — equivalenttypescript
type ID = string | number; // unions, primitives → type
type Status = "active" | "inactive"; // literal unions → type
interface User { // object shapes → interface (merges)
id: string;
name: string;
email?: string;
}
interface AdminUser extends User { roles: string[] }
type AdminUser = User & { roles: string[] }; // intersection — equivalent2. Union Types, Literals, and Narrowing
2. 联合类型、字面量与类型收窄
Discriminated Unions (most important pattern)
可辨识联合(最重要的模式)
typescript
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rectangle": return shape.width * shape.height;
default: return assertNever(shape); // exhaustive
}
}
function assertNever(x: never): never { throw new Error("Unhandled: " + x) }typescript
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rectangle": return shape.width * shape.height;
default: return assertNever(shape); // exhaustive
}
}
function assertNever(x: never): never { throw new Error("Unhandled: " + x) }Narrowing Guards
收窄守卫
typescript
typeof val === "string" // typeof guard
err instanceof Error // instanceof guard
"roles" in user // in guard
val != null // nullish guard
function isAdmin(u: User | AdminUser): u is AdminUser { return "roles" in u }
function assertAdmin(u: User | AdminUser): asserts u is AdminUser {
if (!("roles" in u)) throw new Error("Not admin");
}typescript
typeof val === "string" // typeof guard
err instanceof Error // instanceof guard
"roles" in user // in guard
val != null // nullish guard
function isAdmin(u: User | AdminUser): u is AdminUser { return "roles" in u }
function assertAdmin(u: User | AdminUser): asserts u is AdminUser {
if (!("roles" in u)) throw new Error("Not admin");
}3. Objects and Utility Types
3. 对象与工具类型
typescript
type P = Partial<User>; // all optional
type R = Required<User>; // all required
type RO = Readonly<User>; // all readonly
type D = Omit<User, "id">; // without id
type S = Pick<User, "name" | "email">; // only these
type Rc = Record<"dev"|"prod", Config>; // keyed map
type NN = NonNullable<string | null>; // removes null/undefined
type X = Extract<"a"|"b"|"c", "a"|"c">; // "a" | "c"
type Ex = Exclude<"a"|"b"|"c", "a"|"c">; // "b"typescript
type P = Partial<User>; // all optional
type R = Required<User>; // all required
type RO = Readonly<User>; // all readonly
type D = Omit<User, "id">; // without id
type S = Pick<User, "name" | "email">; // only these
type Rc = Record<"dev"|"prod", Config>; // keyed map
type NN = NonNullable<string | null>; // removes null/undefined
type X = Extract<"a"|"b"|"c", "a"|"c">; // "a" | "c"
type Ex = Exclude<"a"|"b"|"c", "a"|"c">; // "b"4. Deriving Types (DRY Type Design)
4. 类型推导(DRY类型设计)
typescript
type AlbumKeys = keyof Album;
const cfg = { dev: "http://localhost", prod: "https://api.example.com" } as const;
type Env = keyof typeof cfg;
type EnvVals = typeof cfg[keyof typeof cfg];
const ROLES = ["admin", "user", "guest"] as const;
type Role = typeof ROLES[number]; // "admin" | "user" | "guest"
type Params = Parameters<typeof fn>;
type Return = ReturnType<typeof fn>;
type Res = Awaited<ReturnType<typeof asyncFn>>;typescript
type AlbumKeys = keyof Album;
const cfg = { dev: "http://localhost", prod: "https://api.example.com" } as const;
type Env = keyof typeof cfg;
type EnvVals = typeof cfg[keyof typeof cfg];
const ROLES = ["admin", "user", "guest"] as const;
type Role = typeof ROLES[number]; // "admin" | "user" | "guest"
type Params = Parameters<typeof fn>;
type Return = ReturnType<typeof fn>;
type Res = Awaited<ReturnType<typeof asyncFn>>;5. Generics
5. 泛型
typescript
type Result<T, E extends { message: string } = Error> =
| { success: true; data: T }
| { success: false; error: E };
function echo<T>(input: T): T { return input }
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key] }
function createMap<T = string>(): Map<string, T> { return new Map() }
// Conditional
type ToArray<T> = T extends any[] ? T : T[];
// Distributive (applies to each union member)
type DistributiveOmit<T, K extends PropertyKey> = T extends any ? Omit<T, K> : never;
// Mapped
type Nullable<T> = { [K in keyof T]?: T[K] | null };
// Key remapping + template literal
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
// Template literals
type Route = `/${string}`;
type Colors = `${"red"|"blue"}-${100|200|300}`; // 6 combinationstypescript
type Result<T, E extends { message: string } = Error> =
| { success: true; data: T }
| { success: false; error: E };
function echo<T>(input: T): T { return input }
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key] }
function createMap<T = string>(): Map<string, T> { return new Map() }
// Conditional
type ToArray<T> = T extends any[] ? T : T[];
// Distributive (applies to each union member)
type DistributiveOmit<T, K extends PropertyKey> = T extends any ? Omit<T, K> : never;
// Mapped
type Nullable<T> = { [K in keyof T]?: T[K] | null };
// Key remapping + template literal
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
// Template literals
type Route = `/${string}`;
type Colors = `${"red"|"blue"}-${100|200|300}`; // 6 combinations6. Annotations, Assertions, and satisfies
6. 注解、断言与satisfies
typescript
const routes = { home: "/", about: "/about" } satisfies Record<string, `/${string}`>;
routes.home; // type is "/" not string — satisfies keeps narrow type
const user = getUser() as AdminUser; // escape hatch (use sparingly)
const btn = document.getElementById("btn")!; // non-null assertiontypescript
const routes = { home: "/", about: "/about" } satisfies Record<string, `/${string}`>;
routes.home; // type is "/" not string — satisfies keeps narrow type
const user = getUser() as AdminUser; // escape hatch (use sparingly)
const btn = document.getElementById("btn")!; // non-null assertion7. Advanced Type System (Programming TypeScript — Cherny)
7. 高级类型系统(《Programming TypeScript》—— Cherny)
Variance
方差
typescript
// Covariant (producer — can use subtype where supertype expected)
type Producer<T> = () => T;
declare let catProducer: Producer<Cat>;
declare let animalProducer: Producer<Animal>;
animalProducer = catProducer; // OK — Cat is a subtype of Animal
// Contravariant (consumer — can use supertype where subtype expected)
type Consumer<T> = (t: T) => void;
declare let catConsumer: Consumer<Cat>;
declare let animalConsumer: Consumer<Animal>;
catConsumer = animalConsumer; // OK — Animal consumer handles Cat too
// Function types: params are contravariant, return type is covariant
// "Be liberal in what you accept, conservative in what you return"typescript
// Covariant (producer — can use subtype where supertype expected)
type Producer<T> = () => T;
declare let catProducer: Producer<Cat>;
declare let animalProducer: Producer<Animal>;
animalProducer = catProducer; // OK — Cat is a subtype of Animal
// Contravariant (consumer — can use supertype where subtype expected)
type Consumer<T> = (t: T) => void;
declare let catConsumer: Consumer<Cat>;
declare let animalConsumer: Consumer<Animal>;
catConsumer = animalConsumer; // OK — Animal consumer handles Cat too
// Function types: params are contravariant, return type is covariant
// "Be liberal in what you accept, conservative in what you return"The infer
Keyword
inferinfer
关键字
infertypescript
// Extract wrapped type from any container
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type UnwrapArray<T> = T extends Array<infer U> ? U : T;
// Custom ReturnType
type MyReturnType<F extends (...args: any) => any> = F extends (...args: any) => infer R ? R : never;
// Extract first and last tuple elements
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
// Flatten nested arrays
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;typescript
// Extract wrapped type from any container
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type UnwrapArray<T> = T extends Array<infer U> ? U : T;
// Custom ReturnType
type MyReturnType<F extends (...args: any) => any> = F extends (...args: any) => infer R ? R : never;
// Extract first and last tuple elements
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
// Flatten nested arrays
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;Branded / Nominal Types
品牌化/标称类型
typescript
// Prevent mixing structurally identical types (e.g., user ID vs order ID)
type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };
const toUserId = (id: string): UserId => id as UserId;
const toOrderId = (id: string): OrderId => id as OrderId;
function getUser(id: UserId) { /* ... */ }
const uid = toUserId('abc');
const oid = toOrderId('abc');
getUser(uid); // OK
getUser(oid); // Error — OrderId is not UserIdtypescript
// Prevent mixing structurally identical types (e.g., user ID vs order ID)
type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };
const toUserId = (id: string): UserId => id as UserId;
const toOrderId = (id: string): OrderId => id as OrderId;
function getUser(id: UserId) { /* ... */ }
const uid = toUserId('abc');
const oid = toOrderId('abc');
getUser(uid); // OK
getUser(oid); // Error — OrderId is not UserIdOption Type (Null-Safe Pattern)
可选类型(空安全模式)
typescript
type Option<T> = { type: 'some'; value: T } | { type: 'none' };
const some = <T>(value: T): Option<T> => ({ type: 'some', value });
const none: Option<never> = { type: 'none' };
function map<T, U>(opt: Option<T>, fn: (t: T) => U): Option<U> {
return opt.type === 'none' ? none : some(fn(opt.value));
}
function getOrElse<T>(opt: Option<T>, fallback: T): T {
return opt.type === 'none' ? fallback : opt.value;
}
// Usage — no null checks needed
const name = getOrElse(map(getUser(id), u => u.name), 'Anonymous');typescript
type Option<T> = { type: 'some'; value: T } | { type: 'none' };
const some = <T>(value: T): Option<T> => ({ type: 'some', value });
const none: Option<never> = { type: 'none' };
function map<T, U>(opt: Option<T>, fn: (t: T) => U): Option<U> {
return opt.type === 'none' ? none : some(fn(opt.value));
}
function getOrElse<T>(opt: Option<T>, fallback: T): T {
return opt.type === 'none' ? fallback : opt.value;
}
// Usage — no null checks needed
const name = getOrElse(map(getUser(id), u => u.name), 'Anonymous');Returning Exceptions as Union Types
将异常作为联合类型返回
typescript
// Model failure without throwing — callers must handle errors
type DatabaseError = { type: 'DatabaseError'; message: string };
type NotFoundError = { type: 'NotFoundError'; id: string };
async function findUser(id: string): Promise<User | DatabaseError | NotFoundError> {
try {
const user = await db.findById(id);
if (!user) return { type: 'NotFoundError', id };
return user;
} catch (e) {
return { type: 'DatabaseError', message: String(e) };
}
}
// Caller must handle all cases
const result = await findUser('123');
if ('type' in result) {
if (result.type === 'NotFoundError') console.log('Not found:', result.id);
else console.log('DB error:', result.message);
} else {
console.log('User:', result.name); // guaranteed User
}typescript
// Model failure without throwing — callers must handle errors
type DatabaseError = { type: 'DatabaseError'; message: string };
type NotFoundError = { type: 'NotFoundError'; id: string };
async function findUser(id: string): Promise<User | DatabaseError | NotFoundError> {
try {
const user = await db.findById(id);
if (!user) return { type: 'NotFoundError', id };
return user;
} catch (e) {
return { type: 'DatabaseError', message: String(e) };
}
}
// Caller must handle all cases
const result = await findUser('123');
if ('type' in result) {
if (result.type === 'NotFoundError') console.log('Not found:', result.id);
else console.log('DB error:', result.message);
} else {
console.log('User:', result.name); // guaranteed User
}Companion Object Pattern
伴生对象模式
typescript
// Pair an interface with a namespace of the same name
interface Currency { unit: string; value: number }
namespace Currency {
export const from = (value: number, unit: string): Currency => ({ value, unit });
export const add = (a: Currency, b: Currency): Currency => {
if (a.unit !== b.unit) throw new Error('Unit mismatch');
return { value: a.value + b.value, unit: a.unit };
};
export const format = (c: Currency) => `${c.value} ${c.unit}`;
}
const usd = Currency.from(100, 'USD');
const total = Currency.add(usd, Currency.from(50, 'USD'));typescript
// Pair an interface with a namespace of the same name
interface Currency { unit: string; value: number }
namespace Currency {
export const from = (value: number, unit: string): Currency => ({ value, unit });
export const add = (a: Currency, b: Currency): Currency => {
if (a.unit !== b.unit) throw new Error('Unit mismatch');
return { value: a.value + b.value, unit: a.unit };
};
export const format = (c: Currency) => `${c.value} ${c.unit}`;
}
const usd = Currency.from(100, 'USD');
const total = Currency.add(usd, Currency.from(50, 'USD'));Typesafe Event Emitters
类型安全的事件发射器
typescript
// Map event names to their payload types
type Events = {
'user:login': { userId: string; timestamp: Date };
'user:logout': { userId: string };
'order:created': { orderId: string; amount: number };
};
class TypedEventEmitter<T extends Record<string, unknown>> {
private handlers: { [K in keyof T]?: Array<(payload: T[K]) => void> } = {};
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
(this.handlers[event] ??= []).push(handler);
}
emit<K extends keyof T>(event: K, payload: T[K]) {
this.handlers[event]?.forEach(h => h(payload));
}
}
const emitter = new TypedEventEmitter<Events>();
emitter.on('user:login', ({ userId, timestamp }) => console.log(userId, timestamp));
emitter.emit('user:login', { userId: '123', timestamp: new Date() });
// emitter.emit('user:login', { wrongField: true }); // Error!typescript
// Map event names to their payload types
type Events = {
'user:login': { userId: string; timestamp: Date };
'user:logout': { userId: string };
'order:created': { orderId: string; amount: number };
};
class TypedEventEmitter<T extends Record<string, unknown>> {
private handlers: { [K in keyof T]?: Array<(payload: T[K]) => void> } = {};
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
(this.handlers[event] ??= []).push(handler);
}
emit<K extends keyof T>(event: K, payload: T[K]) {
this.handlers[event]?.forEach(h => h(payload));
}
}
const emitter = new TypedEventEmitter<Events>();
emitter.on('user:login', ({ userId, timestamp }) => console.log(userId, timestamp));
emitter.emit('user:login', { userId: '123', timestamp: new Date() });
// emitter.emit('user:login', { wrongField: true }); // Error!Declaration Merging
声明合并
typescript
// What can be merged:
// interface + interface → merged properties
// namespace + namespace → merged members
// class + interface → interface adds instance members
// namespace + function → adds static properties
// namespace + enum → adds methods to enum
interface User { name: string }
interface User { age: number } // merged: { name, age }
// Augment a module's types
declare module 'express' {
interface Request { userId?: string }
}typescript
// What can be merged:
// interface + interface → merged properties
// namespace + namespace → merged members
// class + interface → interface adds instance members
// namespace + function → adds static properties
// namespace + enum → adds methods to enum
interface User { name: string }
interface User { age: number } // merged: { name, age }
// Augment a module's types
declare module 'express' {
interface Request { userId?: string }
}8. React Patterns
8. React模式
typescript
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
children?: React.ReactNode;
}
const Button = ({ label, onClick, variant = "primary" }: ButtonProps) => (
<button className={`btn-${variant}`} onClick={onClick}>{label}</button>
);
const [user, setUser] = useState<User | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value);typescript
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
children?: React.ReactNode;
}
const Button = ({ label, onClick, variant = "primary" }: ButtonProps) => (
<button className={`btn-${variant}`} onClick={onClick}>{label}</button>
);
const [user, setUser] = useState<User | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value);9. Production tsconfig.json
9. 生产环境tsconfig.json
json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"esModuleInterop": true,
"skipLibCheck": true,
"isolatedModules": true,
"declaration": true,
"sourceMap": true,
"outDir": "dist"
}
}strictnoUncheckedIndexedAccessarr[i]T | undefinedjson
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"esModuleInterop": true,
"skipLibCheck": true,
"isolatedModules": true,
"declaration": true,
"sourceMap": true,
"outDir": "dist"
}
}strictnoUncheckedIndexedAccessarr[i]T | undefined10. Top Type-Level Tricks
10. 顶级类型技巧
typescript
type Values<T> = T[keyof T];
type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
type Merge<A,B> = Omit<A, keyof B> & B;
type AsyncReturn<T extends (...args: any) => Promise<any>> = Awaited<ReturnType<T>>;
type Getter<T extends string> = `get${Capitalize<T>}`;
// Strict Omit — only accepts keys that exist in T
type StrictOmit<T, K extends keyof T> = Omit<T, K>;
// Make specific keys required, keep rest as-is
type RequireKeys<T, K extends keyof T> = Required<Pick<T,K>> & Omit<T,K>;typescript
type Values<T> = T[keyof T];
type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
type Merge<A,B> = Omit<A, keyof B> & B;
type AsyncReturn<T extends (...args: any) => Promise<any>> = Awaited<ReturnType<T>>;
type Getter<T extends string> = `get${Capitalize<T>}`;
// Strict Omit — only accepts keys that exist in T
type StrictOmit<T, K extends keyof T> = Omit<T, K>;
// Make specific keys required, keep rest as-is
type RequireKeys<T, K extends keyof T> = Required<Pick<T,K>> & Omit<T,K>;11. Anti-Patterns
11. 反模式
typescript
// BAD: any
function fn(x: any) { return x.val }
// GOOD: unknown + narrowing
function fn(x: unknown) {
if (typeof x === "object" && x && "val" in x) return (x as { val: unknown }).val;
}
// BAD: Omit on unions (collapses to shared properties)
type R = Omit<A | B, "id">; // WRONG — loses unique properties
// GOOD: DistributiveOmit
type R = DistributiveOmit<A | B, "id">;
// BAD: enum (nominal surprise + JS output)
enum Status { Active = "active" }
// GOOD: as const POJO
const Status = { Active: "active" } as const;
type Status = typeof Status[keyof typeof Status];Sources: Total TypeScript — Matt Pocock (No Starch Press, 2026); Ultimate TypeScript Handbook — Dan Wellman (2023); 250 Killer TypeScript One-Liners — Abella; Programming TypeScript — Boris Cherny (O'Reilly, 2019); Effective TypeScript 2nd ed. — Dan Vanderkam (O'Reilly, 2024)
typescript
// BAD: any
function fn(x: any) { return x.val }
// GOOD: unknown + narrowing
function fn(x: unknown) {
if (typeof x === "object" && x && "val" in x) return (x as { val: unknown }).val;
}
// BAD: Omit on unions (collapses to shared properties)
type R = Omit<A | B, "id">; // WRONG — loses unique properties
// GOOD: DistributiveOmit
type R = DistributiveOmit<A | B, "id">;
// BAD: enum (nominal surprise + JS output)
enum Status { Active = "active" }
// GOOD: as const POJO
const Status = { Active: "active" } as const;
type Status = typeof Status[keyof typeof Status];资料来源:《Total TypeScript》—— Matt Pocock(No Starch Press,2026);《Ultimate TypeScript Handbook》—— Dan Wellman(2023);《250 Killer TypeScript One-Liners》—— Abella;《Programming TypeScript》—— Boris Cherny(O'Reilly,2019);《Effective TypeScript 2nd ed.》—— Dan Vanderkam(O'Reilly,2024)
References
参考资料
- — depth on items 50-58 (generics as functions, distribution control, template literal DSLs, type tests, Prettify, tail-recursive types, codegen tradeoffs, soundness traps, compiler perf checklist).
references/generics-and-type-level.md
- — 深入讲解第50-58项内容(泛型作为函数、分发控制、模板字面量DSL、类型测试、Prettify、尾递归类型、代码生成权衡、健全性陷阱、编译器性能检查清单)。
references/generics-and-type-level.md