在Typescript中:是否可能检查预期类型不是某个类型?或者创建一个接口,该接口定义了不应存在的方法/属性?
禁止特定属性
使用never
类型告诉TypeScript一个对象上不应该存在某个属性。
interface Nameless extends Object {
name?: never;
}
使用方法:
declare function foo<T extends Nameless>(argument: T): void;
foo(document); // OK
foo(1); // OK
foo({ name: 'Bob' }); // Error
否定对象类型
使用Subtract
助手:
type Subtract<T, U> = T & Exclude<T, U>
使用方法:
declare function process<T>(argument: Subtract<T, Window>): void;
process(1) // OK
process({}) // OK
process(window) // Error
否定字面量
再次使用Subtract
助手:
type Subtract<T, U> = T & Exclude<T, U>
使用方法:
type Insult =
| 'You silly sod!'
| 'You son of a motherless goat!';
declare function greet<T extends string>(greeting: Subtract<T, Insult>): void;
greet('Hello, good sir!'); // OK
greet('You silly sod!'); // Error!
undefined
,例如 name: undefined
,则 never
无法起作用。 - CMCDragonkaiX
这样的对象形状”情况下使用JSONSchema,或者使用io-ts包来构建这些类型的运行时验证器。 在TypeScript中,您还可以访问type guard函数,用于执行这些验证。
编辑 这是以一种有限且不太有用的方式可能的。使用this article中Omit
类型的修改,类型检查器可以被设置为拒绝一些违反规定的类型,但不是所有。
例如,假设你想要一个类型,表示“任何对象
都没有属性c
或d
”。你可以这样表达这个类型:
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
type AnyTypeWithoutCorD<T> = Omit<T, keyof T & ( "c" | "d" )>
type MyType = { a: number, b: number, c: number };
// Accepts a generic argument and a parameter; ensures the
// parameter can be constrained into the AnyTypeWithoutCorD type.
function funcGeneric<T>(arg: AnyTypeWithoutCorD<T>) {}
// Accepts a parameter of a known type MyType, constrained
// into the AnyTypeWithoutCorD type.
function func(arg: AnyTypeWithoutCorD<MyType>) {}
let foo: AnyTypeWithoutCorD<MyType> = { a: 1, b: 1, c: 2 } // Error: type not assignable
func({ a: 1, b: 1, c: 2 }) // Error: type not assignable
funcGeneric<MyType>({ a: 1, b: 1, c: 2 }) // Error: type not assignable
Restrictions:
MyType
)中的所有键都必须显式枚举。不允许使用索引属性签名;这会导致类型检查器接受Omit
对象,而不考虑它们的字段。例如,如果在上面的示例中将[x: string]: any
添加到MyType
的规范中,则类型检查器将高兴地接受所有参数(& {[x: string]: any}
是等效的,并且行为相同)。如果您正在对其输入类型不受控制的内容执行Omit
类型验证,则意味着如果任何输入类型具有索引属性签名,则您的验证将不会发生。Omit
类型构造函数提供全新值或在类型检查中进行了类似“早期”的操作时,此方法才有效。从先前的赋值或通过类型断言提供数据不会按预期执行Omit
验证。Omit
类型的通用函数或类型提供原始数据,并且在调用函数时未明确声明泛型,则不会正确推断泛型,因此您的类型将无法通过,Omit
验证也将不会发生。let bar: MyType = { a: 1, b: 1, c: 2 }
func(bar)
func({ a: 1, b: 1, c: 2 } as MyType)
funcGeneric(bar)
funcGeneric({ a: 1, b: 1, c: 2 })
let ok: AnyTypeWithoutCorD<object> = { a: 1, b: 1, c: 2 }
let ok: AnyTypeWithoutCorD<{ c: number }> = { a: 1, b: 1, c: 2 }
let ok: AnyTypeWithoutCorD<MyType> = { a: 1, b: 1, c: 2 } as MyType
let ok: AnyTypeWithoutCorD<MyType> = bar as MyType
这是我首次尝试实现/演示这个目标,因此更熟悉TypeScript和自定义类型构造的人可能会纠正我。请带着怀疑的态度。
结论:
Omit
的解决方案不值得。
除非您控制对Omit
接收器的所有输入类型,并保持这些输入类型不包含索引属性签名的约束,还要确保每次向其中一个接收器提供任何内容时,它实际上采用了 Omit
约束,否则,这将造成更多损害而非裨益。
这是因为它有时会正确验证您的类型,从而创建虚假的安全感和难以调试的、似乎不一致的行为。
如果完成了问题的第一部分中链接的问题,将会得到一个更加强大、可预测和有文档支持的解决方案。在那之前,请使用运行时检查。
function foo(bar: string | number){
if (typeof bar !== 'string') {
// TypeScript knows its a number!
return bar.toPrecision(3);
}
}
类型守卫:https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html