Reputation: 36219
Let's say I have a type
interface Test {
foo: number;
bar?: {
name: string;
};
}
const obj: Test;
// obj.bar is optional right now
But let's say I have doSomething(obj)
and this method will make bar
required and set it. I want doSomething
to then return a new type where bar
is required now.
Of course, I want this to be dynamic - as in, I don't want to create interface TestRequired
and manually return that type.
Is this possible in Typescript?
Upvotes: 1
Views: 618
Reputation: 250016
The simplest way to change a property from optional to required is to intersect the original interface with a type where the containing only that property, but required. You can easily create such a type using Required
and Pick
:
declare const obj2: Test & Required<Pick<Test, 'bar'>>;
obj2.bar.name // bar is required so this is ok
Also note that if your function changes an existing object, you can use a custom type assertion to change the original type:
const obj: Test = { foo: 0}
let a = obj.bar // a is { name: string; } | undefined
obj.bar.name // err
function doSomething(o: Test): asserts o is Test & Required<Pick<Test, 'bar'>> {
o.bar = { name: ""}
}
doSomething(obj)
obj.bar.name // ok
You could also make a function that adds bar
but a type assertion is necessary:
function addBar(o: Test) {
o.bar = { name: "" }
return o as Test & Required<Pick<Test, 'bar'>> ;
}
let obj = addBar({ foo: 0 });
obj.bar.name;
Upvotes: 4
Reputation: 32186
Of course. You can add or remove the optional modifier by property name or generally:
// StdLib already has the the type Partial<T> and Required<T> to make all properties of T optional or required respectively.
// Makes the named properties of T optional:
type PartialSome<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
// Makes the named properties of T required:
type RequiredSome<T, K extends keyof T> = T & Required<Pick<T, K>>
So your example might then look like this:
interface Test {
foo: number;
bar?: {
name: string;
};
}
declare function doSomething<T extends {bar?: any}>(obj: T): RequiredSome<T, "bar">;
const obj: Test;
const other = doSomething(obj);
other.bar // bar is required!
Upvotes: 3