TypeScript中Omit工具类型异常:属性变any且未触发预期错误
TypeScript泛型类型修改问题
场景一:移除Context接口的one属性并透传泛型
以下是复现代码及预期行为注释:
/******************* * First Situation * *******************/ type DefaultContext = Record<string, any>; const logger = (str?: string | Record<string, unknown> | any[]) => console.log(str); interface Context<T extends Record<string, unknown> = Record<string, unknown>> extends DefaultContext { one?: T; many?: T[]; } function oldProcess< T extends Record<string, unknown> = Record<string, unknown> >(context: Context<T>) { const many = context.many; // T[] | undefined logger(many); const one = context.one; // T | undefined logger(one); } type BadContext<T extends Record<string, unknown> = Record<string, unknown>> = Omit<Context<T>, "one">; function badProcess< T extends Record<string, unknown> = Record<string, unknown> >(context: BadContext<T>) { const many = context.many; // expected `T[] | undefined` received `any` logger(many); const one = context.one; // expected error received `any` ❌ logger(one); // expected error ❌ } type AsiContext<T extends Record<string, unknown> = Record<string, unknown>> = Context<T> & { one: never }; function asiProcess< T extends Record<string, unknown> = Record<string, unknown> >(context: AsiContext<T>) { const many = context.many; // T[] | undefined logger(many); const one = context.one; // never 😐 logger(one); // want error ❌ }
问题:
- 使用
Omit创建的BadContext将所有属性转为any类型,且仍允许访问已移除的one属性,不符合预期。 - 使用交叉类型
{one: never}创建的AsiContext虽保留了原有属性类型,但传递never类型的one时未触发预期错误。
场景二:移除TArgs类型的uid属性、透传泛型并将uids设为必填
复现代码及预期行为注释:
/******************** * Second Situation * ********************/ type NodeObj = Record<string, unknown> & { uid?: string; type?: string; }; type Entity<T extends Record<string, unknown> = Record<string, unknown>> = NodeObj & T & { changed?: string; created?: string; }; type TArgs<E extends Entity = Entity> = Partial<E> & { uids?: string[] }; const foo: TArgs = { uid: undefined, type: "User", uids: ["0x2a", "0x2b"], }; // Create a new Type that should keep TArgs typed but Omit `uid` type BadTArgs<T extends TArgs = TArgs> = Omit<T & { uids: string[] }, "uid">; const bar: BadTArgs = { uid: 42, // expected error as prop is now omitted ❌ type: 42, // expected error as prop should be `string` ❌ uids: 42, // expected error as ids should be `string[]` ❌ }; // Create a new Type that should keep TArgs typed but force `uid` to be `never` type AsiTArgs<T extends TArgs = TArgs> = T & { uids: string[] } & { uid?: never; }; const baz: AsiTArgs = { uid: undefined, // want error ❌ type: "User", // ✅ uids: ["0x1", "0x2"], // ✅ }; const fooType = foo.type; // string | undefined ✅ const barType = bar.type; // unknown ❌ const bazType = baz.type; // string | undefined ✅ const fooUid = foo.uid; // string | undefined ✅ const barUid = bar.uid; // unknown ❌ expected error const bazUid = baz.uid; // undefined ❌ expected never
问题:
- 使用
Omit创建的BadTArgs丢失了原有类型约束,所有属性类型异常,且允许设置已移除的uid属性。 - 使用交叉类型
{uid?: never}创建的AsiTArgs虽保留了其他属性类型,但允许设置uid: undefined,未触发预期错误。
内容的提问来源于stack exchange,提问作者amaster
相关产品推荐
相关产品推荐

