Make a single property optional in TypeScript
Solution 1
You can also do something like this, partial only some of the keys.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = PartialBy<Person, 'nickname'>
Solution 2
Here is my Typescript 3.5+ Optional utility type
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
// and your use case
type MakePersonInput = Optional<Person, 'nickname'>
// and if you wanted to make the hometown optional as well
type MakePersonInput = Optional<Person, 'hometown' | 'nickname'>
Solution 3
For a plug and play solution, consider using the brilliant utility-types
package:
npm i utility-types --save
Then simply make use of Optional<T, K>
:
import { Optional } from 'utility-types';
type Person = {
name: string;
hometown: string;
nickname: string;
}
type PersonWithOptionalNickname = Optional<Person, 'nickname'>;
// Expect:
//
// type PersonWithOptionalNickname {
// name: string;
// hometown: string;
// nickname?: string;
// }
Solution 4
Update:
As of TypeScript 2.8, this is supported much more concisely by Conditional Types! So far, this also seems to be more reliable than previous implementations.
type Overwrite<T1, T2> = {
[P in Exclude<keyof T1, keyof T2>]: T1[P]
} & T2;
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = Overwrite<Person, {
nickname?: string;
}>
function makePerson(input: MakePersonInput): Person {
return {...input, nickname: input.nickname || input.name};
}
As before, MakePersonInput
is equivalent to:
type MakePersonInput = {
name: string;
hometown: string;
} & {
nickname?: string;
}
Outdated:
As of TypeScript 2.4.1, it looks like there's another option available, as proposed by GitHub user ahejlsberg in a thread on type subtraction: https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = Overwrite<Person, {
nickname?: string
}>
function makePerson(input: MakePersonInput): Person {
return {...input, nickname: input.nickname || input.name};
}
According to Intellisense, MakePersonInput
is equivalent to:
type MakePersonInput = {
name: string;
hometown: string;
} & {
nickname?: string;
}
which looks a little funny but absolutely gets the job done.
On the downside, I'm gonna need to stare at that Diff
type for a while before I start to understand how it works.
DallonF
Updated on October 22, 2021Comments
-
DallonF over 2 years
In TypeScript, 2.2...
Let's say I have a Person type:
interface Person { name: string; hometown: string; nickname: string; }
And I'd like to create a function that returns a Person, but doesn't require a nickname:
function makePerson(input: ???): Person { return {...input, nickname: input.nickname || input.name}; }
What should be the type of
input
? I'm looking for a dynamic way to specify a type that is identical toPerson
except thatnickname
is optional (nickname?: string | undefined
). The closest thing I've figured out so far is this:type MakePersonInput = Partial<Person> & { name: string; hometown: string; }
but that's not quite what I'm looking for, since I have to specify all the types that are required instead of the ones that are optional.
-
DallonF about 7 yearsHi... so... this is a great response to the question I asked, but not so much for what I'm actually trying to solve :/ Sorry for the oversimplification of the question. In the terms of what I asked... basically, every Person must have a nickname, but I don't want this function to require it. If it's not provided, it'll just use the
name
input. -
Brian Gorman about 7 yearsI edited my answer to better illustrate the second option I was advocating. Let me know if we still aren't on the same page? Notice that the RegularPerson constructor makes the "nickname" value equal to the "name" value of input.
-
DallonF about 7 years
input
here is still missing a type, which is the tricky part of this question... I think what I'm trying to do just isn't possible in TypeScript yet. I think I'm going to self-answer with what I found. -
joshhunt almost 6 yearsNote that this makes any optional fields in the original interface required
-
andrey over 5 yearsuse this to keep optional fields - type Overwrite<T1, T2> = Pick<T1, Exclude<keyof T1, keyof T2>> & T2;
-
epere4 about 5 yearsThis is awesome! Note you can pass more than one property: type OnlyNameIsMandatory = PartialBy<Person, 'nickname'|'hometown'>
-
Sly_cardinal almost 5 yearsA quick note, the
Omit
helper type is now available by default in TypeScript 3.5+. So if you are using TS 3.5 or above you no longer need to defineOmit
yourself. -
Thanos M about 4 yearswhat if I also want to add some extra properties to MakePersonInput?
-
Brachacz about 3 years@ThanosM you can use
interface PersonExtended extends MakePersonInput { some_prop: string; }
or intersection typetype MakePersonInput = PartialBy<Person, 'nickname'> & {some_prop: string}
-
electrovir about 3 years
Pick<T,K>
actually doesn't seem to be necessary:Omit<T, K> & Partial<T>
gets the job done as well. -
inttyl about 3 yearsHi, how about nested atrribute something like If there an attribute object that need to be partial , how can i do it ? For exemple, I want address to be partial ` interface Person { name: string; hometown: string; nickname: string; address: Address } interface Address { street: string, city: string } `
-
Sebastiandg7 about 2 yearsThis is beautyful!
-
André Vendramini almost 2 yearsAwesome! Would you mind to explain the logic behind it?
-
Tim Krins almost 2 yearsSure!
Optional
takes two arguments,T
is the type we want to base our optional type, andK
represents a set of keys that are available on the typeT
.Partial<T>
returns a type with all of the keys inT
marked as optional. Surrounding thePartial<T>
with aPick<...,K>
gives us a type with only the keys that we supplied, which we have already made optional. UsingOmit<T,K>
gives us a type without any of the keys that we have specified. By using an&
, it will union the two types together. -
Tim Krins almost 2 yearsMulling it over more, there might be a shorter way of accomplishing the same thing - but feels cleaner in my mind building two types with no overlapping keys and merging them together.