I have the following object with a const assertion:
const foo = {
bar: ['a', 'b'],
} as const;
My goal is to write a function that updates the bar
array and correctly infers the new type.
I can achieve the intended result when I pass foo.bar
into the function:
type Bar = Readonly<string[]>;
function update<T extends Bar>(arr: T) {
return {
bar: [...arr, 'c'],
} as const;
}
const updatedFoo = update(foo.bar);
// Inferred type is as expected:
//
// const updatedFoo: {
// readonly bar: readonly ["a", "b", "c"];
// }
But I cannot get this to work when I pass in foo
itself:
type Foo = Readonly<{ bar: Bar }>;
function update2<T extends Foo>(obj: T) {
return {
bar: [...obj.bar, 'c'],
} as const;
}
const updatedFoo2 = update2(foo);
// Inferred type is too wide:
//
// const updatedFoo2: {
// readonly bar: readonly [...string[], "c"];
// }
How can I rewrite update2
to correctly infer the type of bar
as readonly ["a", "b", "c"]
?
CodePudding user response:
The simplest solution would probably to continue using T
to represent the type of bar
. We can give the parameter obj
an object type having a property bar
of type T
.
function update2<T extends Bar>(obj: { bar: T }) {
return {
bar: [...obj.bar, 'c'],
} as const;
}
const updatedFoo2 = update2(foo);
// const updatedFoo2: {
// readonly bar: readonly ["a", "b", "c"];
// }
If you want to use the whole obj
type as T
, you will have to use a type assertion.
function update2<T extends Foo>(obj: T) {
return {
bar: [...obj.bar as T["bar"], 'c'],
} as const;
}