1

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"]?

1 Answer 1

1

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;
}

Playground

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.