简体   繁体   English

“该类型上不应存在成员”的 TypeScript 类型声明?

[英]TypeScript type declaration for "member should not exist on this type"?

Is there a way in a TypeScript record type to assert that the type must not contain a property with a particular name?有没有办法在 TypeScript 记录类型中断言该类型不能包含具有特定名称的属性?

The use case is where we're using the presence of a particular property at runtime to tell the difference between an object that implements a particular interface vs. a property bag for specifying named arguments.用例是我们在运行时使用特定属性的存在来区分实现特定接口的对象与用于指定命名参数的属性包之间的区别。

For example: TS playground link例如: TS Playground 链接

interface Foo {
  readonly bar: string;
}
function f(arg: Foo | {foo: Foo}) {
  // get a Foo instance, either as the argument or as a named arg
  const foo: Foo = 'foo' in arg ? arg.foo : arg;
  return foo.bar;
}

We'd like to warn implementers of the Foo interface that they must not have a property called foo on their type.我们想警告Foo接口的实现者,他们的类型不能有名为foo的属性。 For example, we'd like this code to cause a compile error:例如,我们希望这段代码导致编译错误:

class MyFoo1 implements Foo {
  foo: Foo = { get bar() { return 'abc'; } }; // wanted: compile error here
  get bar() { return this.foo.bar; }
}
class MyFoo2 implements Foo {
  foo: string = 'bar'; // wanted: compile error here
  get bar() { return this.foo; }
}
// no compiler errors expected from this class
class MyFoo3 implements Foo {
  get bar() { return 'hello'; }
}

Can we do this?我们可以这样做吗?

The possible solutions I've considered are:我考虑过的可能解决方案是:

  1. never - This one will produce an error, but it also claims that this property actually exists, which seems wrong. never - 这个会产生错误,但它也声称这个属性确实存在,这似乎是错误的。
interface Foo {
  foo: never;
  bar: string;
}
  1. undefined - This seems closer to what we want, but won't it still show up in IDE autocomplete when it never exists? undefined - 这似乎更接近我们想要的,但是当它从不存在时,它不会仍然显示在 IDE 自动完成中吗?
interface Foo {
  foo?: undefined;
  bar: string;
}
  1. conditional type - This one seems the most promising, but I'm not sure how to make a non-generic conditional type so that callers could say class MyFoo2 implements Foo not class MyFoo2 implements Foo<SomethingElse> .条件类型 - 这似乎是最有前途的,但我不确定如何制作非通用条件类型,以便调用者可以说class MyFoo2 implements Foo而不是class MyFoo2 implements Foo<SomethingElse>

The trick you want is:你想要的技巧是:

interface Foo {
  foo?: never
  readonly bar: string;
}

This tells typescript that foo either must be omitted, or it must have a type of never , which is never allowed.这告诉打字稿是foo要么必须被省略,或者它必须有一个类型的never ,这是绝对不允许。 This reduces to just the fact that it must be omitted.这简化为必须省略它的事实。

Playground 操场


Note that I did have to change your runtime code a tiny bit to get this to compile.请注意,我确实必须稍微更改您的运行时代码才能编译它。

const aFoo: Foo = ('foo' in arg && arg.foo) ? arg.foo : arg;

Typescript wasn't quite smart enough to infer that the branch of the union there, because it's hard to tell the difference at runtime from an omitted prop and a prop with a value of undefined . Typescript 不够聪明,无法推断出联合的分支,因为在运行时很难区分省略的 prop 和值为undefined的 prop。 By forcing it to check that arg.foo has a truthy value it then knows that arg must be of type { foo: Foo } .通过强制它检查arg.foo是否有一个真值,它就知道arg必须是{ foo: Foo }类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM