Description
TypeScript Version: 2.9.1-insiders.20180516
Search Terms:
mapped types
conditional mapped types
conditional required types
filter mapped types
Code
// Utility types
type Entity<T> = T;
type Eager<T> = {
[P in keyof T]-?: T[P] extends Entity<infer U> ? Eager<U> : never;
} & {
[P in keyof T]: T[P] extends Entity<infer U> ? never : T[P];
}
// Database models
type Person = {
firstName: string;
lastName: string;
addresses?: Entity<Address[]>;
}
type Address = {
line1: string;
town: string;
telephoneLine: Entity<TelephoneLine>;
}
type TelephoneLine = {
phoneNumber: string;
features?: {
hasBroadband: boolean;
}
}
declare var person: Person;
person.addresses[0] // EXPECTED - addresses is possibly undefined
declare var eagerLoadedPerson: Eager<Person>
eagerLoadedPerson.addresses[0].line1; // should work fine but doesn't
eagerLoadedPerson.addresses[0].telephoneLine.phoneNumber; // should work fine but doesn't
eagerLoadedPerson.addresses[0].telephoneLine.features.hasBroadband // EXPECTED - should give error because "features" is not an entity so the ? modifier should remain
Expected behavior:
I would expect the above code example to work (except for the EXPECTED errors as described). Well, not exactly the above code example, I appreciate the Eager type is a bit messed up.
Actual behavior:
Doesn't work - I can't seem to get a utility type Eager that will make all marked optional properties mandatory. In fact, forget marked properties. I can't even write an Eager type that will remove the optional modifier from all properties in the tree. For example the following example doesn't work either:
// Utility types
type Entity<T> = T;
type primitive = string | number | boolean | null | undefined;
type Eager<T> = NonNullable<{
[P in keyof T]: T[P] extends primitive ? T[P] : Eager<T[P]>;
}>
Related issues
#23199 - I think this one is related but the filtering is finally achieved by using the property type - I can't seem to do that.
What I'm trying to achieve
We are using the sequelize ORM and we define some relationships on our models. When we query for our model, if we don't fetch the relationship then it will come back undefined. So we want our base type to represent that. However, sometimes we query for the whole tree so we'd like a utility type that will mark all those relation properties as being present.