简体   繁体   English

带接口的交集类型无法抛出 TypeScript 错误

[英]Intersection types with interfaces fails to throw TypeScript error

By mixing TypeScript types with interfaces in an intersection, I seem to loose the stricter behaviour of interfaces in my code.通过在交叉点中将 TypeScript 类型与接口混合,我似乎放松了代码中接口的更严格行为。 I want to be able to compose types using intersections (such that I can narrow down a type in specific instances), but I want to be able to keep the exactness of interfaces.我希望能够使用交集来组合类型(这样我可以在特定情况下缩小类型),但我希望能够保持接口的准确性。

Take this code:拿这个代码:

type Vehicle = {
  name: string
  properties: Record<string, unknown>
}

interface CarProperties {
  electric?: boolean
}

type Car = Vehicle & {
  name: 'car'
  properties: CarProperties
}

const car: Car = {
  name: 'car',
  properties: {
    electric: false,
    someOther: 'bs' // <= I want this to throw a TS Error
  }
}

As the CarProperties is an interface, I would expect it to disallow the inclusion of the someOther key.由于CarProperties是一个接口,我希望它不允许包含someOther键。

Why is this not the case?为什么不是这样? And how else would one go about achieving the same thing?还有一个 go 如何实现同样的目标?

The problem is the intersection type Car .问题是路口类型Car This leads to properties of car being a Record<string, unknown> & CarProperties and thus allows string keys.这导致 car 的propertiesRecord<string, unknown> & CarProperties ,因此允许字符串键。

To achieve what you want, you can add a generic to your Vehicle type:为了实现您想要的,您可以将泛型添加到您的车辆类型:


type Vehicle<T extends object> = {
  name: string
  properties: T
}

type GenericVehicle = Vehicle<Record<string, unknown>>;

interface CarProperties {
  electric?: boolean
}

interface Car extends Vehicle<CarProperties> {
  name: 'car'
}

PS: I noticed you call this a "union type", which it is not . PS:我注意到您将其称为“联合类型”, 但事实并非如此 Intersection types share the properties of the intersected types, whereas union types can be either of the unionized types.交叉类型共享交叉类型的属性,而联合类型可以是联合类型中的任何一种。

You can make electric property a required one and use object type instead of Record<string, unknown> .您可以将electric属性设为必需属性,并使用object类型而不是Record<string, unknown>

type Vehicle = {
    name: string
    properties: object, // <---- change
}

type CarProperties = {
    electric: boolean // is required now
}

type Car = Vehicle & {
    name: 'car'
    properties: CarProperties
}


// ok
const car2: Car = {
    name: 'car',
    properties: {
        electric: false,
    }
}

// expected error
const car4: Car = {
    name: 'car',
    properties: []
}

// expected error
const car5: Car = {
    name: 'car',
}

// error
const car3: Car = {
    name: 'car',
    properties: {}
}

// error
const car: Car = {
    name: 'car',
    properties: {
        electric: false,
        someOther: 'bs' // error
    }
}

I know, using object type is controversial a bit, because of this rule, but you can find more information about pros and cons in this answer.我知道,由于这条规则,使用object类型有点争议,但你可以在这个答案中找到更多关于利弊的信息。

If you have more variants of Vehicle , not only one Car , you may want to remove properties from Vehicle type and create a discriminated union of allowed Vehicles .如果您有更多的Vehicle变体,而不仅仅是一个Car ,您可能希望从Vehicle类型中删除properties并创建允许的Vehicles的有区别的联合。 It is only my guess, since I'm not aware of any other requirements.这只是我的猜测,因为我不知道任何其他要求。

It is also worth knowing about excess-property-checks .还值得了解有关extra-property-checks 的信息。 It will disallow you to provide any other extra properties for literal argument.它将不允许您为文字参数提供任何其他额外属性。

If you put all your requirements and test cases into your question I believe you will get what you are looking for如果您将所有要求和测试用例都放入您的问题中,我相信您会得到您想要的

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

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