Home > OS >  Typescript object literal intersection
Typescript object literal intersection

Time:08-24

say we have the following code:

type neverHanppens = string & object // never

type A = {
    name:'A'
}

type B = {
    nickName:'B'
}

const a:A = {
    name:'A',
    nickName:'B' // error:'nickName' does not exist in type 'A'.
}

const c:A & B = {
    name:"A",
    nickName:"B"
}

I think in typescript , A & B means a type is A and also is B at the same time, is there a type that is both a string and a object ? no, so string & object equals never, that make sense to me.

but how about the literal object intersection above? as you can see, type A and B have not any property name in common, so it should be never. but why type A & B covers both property name and nickName? and you can see the variable a we defined that way we would got a error, so {name:'A', nickName:'B'} is not a type A, so it against the logical (are both type) above.

if we conclude that A & B means a type will cover A and B , then string & object shouldn't be never, it should means type string and type object.

could someone explain what's wrong with my logic?

CodePudding user response:

Object types in TypeScript are open or extendible, not closed or exact (as requested in ms/TS#12936). Object types only specify which properties should be present, they do not enforce that unmentioned properties are absent. So a type like

type A = {
    name: 'A'
}

just implies that an A has to have a name property whose type is "A". It does not imply that an A has only a name property. Object types are extendible and you can always add properties to them without breaking them:

interface AlsoA extends A {
    nickName: string;
}
const alsoA: AlsoA = {
    name: 'A',
    nickName: 'B'
}
const stillA: A = alsoA; // okay

The confusion you are having stems from the fact that object literals undergo excess property checking, resulting in the warning you saw when you directly assign an object literal with an extra property to a variable whose type doesn't know about it:

const a: A = {
    name: 'A',
    nickName: 'B' // error:  Object literal may only specify known properties
}

The problem isn't that {name: "A", nickName: "B"} isn't a valid A (despite what the error might say). It is a valid A. The problem is that the object literal {name: "A", nickName: "B"} is immediately assigned to a variable of type A which has no knowledge of nickName. The bug being caught here is that someone is probably accidentally throwing away information. It's not a type safety issue; it's more of a linter warning.


Now that we know that object types are open and extendible, then the intersection type A & B should be both an A and a B, which means it must have a name property whose type is "A" and a nickName property whose type is "B". This is not equivalent to never, as A does not imply the absence of a nickName property, and B does not imply the absences of a name property.

Playground link to code

CodePudding user response:

Object types represent the minimal requirements for an object. So type A means any object that has a name property. The type does not forbid any other properties from being present though.


type A = {
    name:'A'
}

const anonymous = {
    name:'A',
    nickName:'B'
} as const

const a: A =  anonymous //ok

Playground Link

Generally a type that has extra properties, will be a subtype of the original type and thus will be assignable to the original type

The error you are getting when you assign an object with more properties to the variable typed as A is called excess property checks, and is actually a useful if confusing exception to the rule I stated above. Useful, because if you are assigning a fresh object literal to a variable or parameter with a specific type, you probably don't want extra properties, but confusing because it gives the impression object types are a complete description of the object that may be assigned to the object type (which as we say above it is not)

So given this understanding, A & B now makes more sense. The intersection of a type that must have name (but may have any other properties including nickname) and a type that must have nickname (but may have any other properties including name) is a type that must have name and nickname (but may have any other properties)

  • Related