atconway
atconway

Reputation: 21304

Why does keyof yield a union of properties rather than an intersection of properties?

Using keyof with the following type yields a union of property names:

type Person = {
    name: string;
    age: number;
    location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"

Similarly, if I use the Omit helper that leverages Exclude, I must provide a union of properties for keyof T where intuitively I'd expect to provide an intersection of properties to be omitted but that's not the case:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type PersonName = Omit<Person, "location" | "age">;  
// type Person = { name: string }

I'd think from my understanding, it would be the following instead which is the composition of the type as it requires the intersection of all properties and not the union of the properties:

// This isn't valid, but what I'd expect using keyof
type K1 = keyof Person; // "name" & "age" & "location"
type PersonName = Omit<Person, "location" & "age">;

When it comes to keyof, where am I off in my understanding of the properites yielded as a union as opposed to an intersection of the property keys for that type? The type can't exist with each property being an either scenario as a union describes. The type isn't valid unless it's the intersection of all properties correct?

Upvotes: 0

Views: 175

Answers (1)

artem
artem

Reputation: 51629

The type can't exist with each property being an either scenario as a union describes.

keyof T does not describe type T, it describes a type for possible keys that can be used to get properties of T, that is, a type for key variable in the expression t[key] where t value has type T. This is the type system for javascript, after all, where property access t.propertyName is exactly the same as slightly more elaborated t["propertyName"], and the latter has always allowed accessing object properties dynamically using a variable that holds a property name.

It's a union type because you can use any single key in the t[key] expression.

Intersection of literal types just does not make sense - type "name" & "age" & "location" means that a variable value is equal to all three literal values at the same time, which is impossible. Internally such intersections are reduced to never type.

Upvotes: 2

Related Questions