Typescript 负类型检查

13
在Typescript中:是否可能检查预期类型不是某个类型?或者创建一个接口,该接口定义了不应存在的方法/属性?

6
请提供一两个您希望失败的声明/定义的示例,并描述为什么您想这样做? 能否提供一到两个您希望出错的声明/定义示例,并说明原因? - user663031
这里的问题不是很清楚,但是,如果有人在网上发现了这个问题,我已经发布了一个可能相关的解决方案:https://dev59.com/VrDma4cB1Zd3GeqPALEU#53917039 - Babakness
3个回答

8

禁止特定属性

使用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!

1
如果您想防止将属性设置为 undefined,例如 name: undefined,则 never 无法起作用。 - CMCDragonkai

1
在TypeScript编译器中目前还不能做到这一点,不过以后可能会有可能。 TypeScript项目上的这个问题 似乎是跟踪此功能请求的主要问题;许多其他类似的问题都与其链接。
总之,可以在运行时进行这些检查。这可以通过标准的检查/断言手动完成。我个人喜欢在非平凡的“除了像X这样的对象形状”情况下使用JSONSchema,或者使用io-ts包来构建这些类型的运行时验证器。 在TypeScript中,您还可以访问type guard函数,用于执行这些验证。

编辑 这是以一种有限且不太有用的方式可能的。使用this articleOmit类型的修改,类型检查器可以被设置为拒绝一些违反规定的类型,但不是所有。

例如,假设你想要一个类型,表示“任何对象都没有属性cd”。你可以这样表达这个类型:

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 约束,否则,这将造成更多损害而非裨益

这是因为它有时会正确验证您的类型,从而创建虚假的安全感和难以调试的、似乎不一致的行为。

如果完成了问题的第一部分中链接的问题,将会得到一个更加强大、可预测和有文档支持的解决方案。在那之前,请使用运行时检查。


-4
在 TypeScript 中,是否有可能检查预期类型不是某个类型?
我猜你想要的是运行时类型。由于 JavaScript 没有标准化的方法来实现这一点,因此 TypeScript 也没有办法做到。
如果你只是想要简单的类型保护,那么它们可以正常工作,例如:
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


1
这是另一件有点不同的事情。 - WHITECOLOR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接