Reputation: 29109
I want to type an object which can only have keys 'a', 'b' or 'c'.
So I can do it as follows:
Interface IList {
a?: string;
b?: string;
c?: string;
}
They are all optional!
Now I was wondering if this can be written with Record
in just one line
type List = Record<'a' | 'b' | 'c', string>;
The only issue is that all keys need to be defined. So I ended up with
type List = Partial<Record<'a' | 'b' | 'c', string>>;
This works, but I can imagine there is a better way to do this without Partial. Is there an other way to make the keys optional inside Record ?
Upvotes: 155
Views: 121478
Reputation: 163
Check if this meet your requirements:
type SomeType = Partial<Record<keyof IList, string>>
You can update IList to define new valid keys. And they will be optional.
Upvotes: 2
Reputation: 3753
You can also use
type Key = "a" | "b" | "c"
type List = { [key in K]?: string }
Upvotes: 4
Reputation: 647
It should be noted that all (valid) solutions to the question also allow empty Records to be passed. If you want to enforce at least a specific key you need:
interface Foo {
a: A,
[key: string]: B
}
Upvotes: 2
Reputation: 1130
It's also possible to do it this way:
type List = { [P in 'a' | 'b' | 'c']?: string }; // The `?` makes the keys optional
Examples:
const validList1: List = {
a: 'hi'
}
const validList2: List = {
b: 'hi',
c: 'there'
}
const validList3: List = {
a: 'oh',
b: 'hi',
c: 'there'
}
Upvotes: 15
Reputation: 789
Looks like in new versions of typescript you may do the following
type YourUnion = 'a' | 'b' | 'c';
type ObjectWithOptionalKeys = Partial<Record<YourUnion, string>>
const someObject: ObjectWithOptionalKeys {
a: 'str', // works
b: 1 // throws
}
// c may not be specified at all
Upvotes: 34
Reputation: 24710
Aside from the Partial<Record<List, string>>
solution,
there is perhaps a more obvious option to consider.
Instead, you could store your data in a map.
const map: Map<KeyType, ValueType> = new Map();
From a functional point of view there's not much difference. It really depends on the context whether this is a viable alternative or not.
Upvotes: 7
Reputation: 250882
You can create the partial version of your List
type:
type PartialList = Partial<List>;
And you could do it all on one line if you don't want the intermediate type:
type PartialList = Partial<Record<'a' | 'b' | 'c', string>>;
You might decide that, in the end, the most expressive for your future self is:
type List = {
a?: string;
b?: string;
c?: string;
}
Upvotes: 85
Reputation: 249536
There is no way to specify the optionality of members of Record
. They are required by definition
type Record<K extends keyof any, T> = {
[P in K]: T; // Mapped properties are not optional, and it's not a homomorphic mapped type so it can't come from anywhere else.
};
You can define your own type if this is a common scenario for you:
type PartialRecord<K extends keyof any, T> = {
[P in K]?: T;
};
type List = PartialRecord<'a' | 'b' | 'c', string>
Or you can define PartialRecord
using the predefined mapped types as well:
type PartialRecord<K extends keyof any, T> = Partial<Record<K, T>>
Upvotes: 198