Alexander Zeitler
Alexander Zeitler

Reputation: 13089

Copy property from other type

I have a type Command

export type Command<
  Type extends string = string,
> = {
  type: Type
}

I'm creating a type Handler like this:

export type Handler<T extends Command> = {
  type: Pick<T, 'type'>
}

The goal is to make sure that the type of the returned Handler here is the same as for the command:

export type MyCommand = Command<'MyCommand'>

const MyHandler = (): Handler<MyCommand> => {
  return {
    type: 'MyCommand'
  }
}

But I'm getting this error:

Type 'string' is not assignable to type 'Pick<MyCommand, "type">'.(2322)

I've created TS playground here.

Upvotes: 0

Views: 91

Answers (2)

Mr. Hedgehog
Mr. Hedgehog

Reputation: 2885

You can infer Type generic type from Command inside Handler like this:

export type Handler<T> = T extends Command<infer Type> 
  ? {type: Type} 
  : never;

Following description is how I understand inference in this case.

Here we have ternary type where we check that T in fact extends Command. If it doesn't extends Command, we return never, as it should never happen. But if it does, we infer (get type) value of Command generic and put it into some kind of type variable Type. Then we return {type: Type} as a value of our Handler.

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

Pick<T, K> creates an object type that contains the properties specified in K. So Pick<MyCommand, 'type'> is actually { type: 'MyCommand' }

If you just want the type of a property in another type use an index accessed type T[K]

export type Command<
  Type extends string = string,
> = {
  type: Type
}

export type Handler<T extends Command> = {
  type: T['type']
}

export type MyCommand = Command<'MyCommand'>

const MyHandler = (): Handler<MyCommand> => {
  return {
    type: 'MyCommand'
  }
}

Playground Link

Upvotes: 1

Related Questions