Reputation: 2121
I'm really sorry for the title but I actually don't know what I'm looking for. I have a type with 2 properties and I want one of those to be related to the other one.
Here's an example:
My first type which is a map of types:
type TextApiFormat = {
title: string
}
type ImageApiFormat = {
src: string
}
export type ApiFormats = {
text: TextApiFormat
image: ImageApiFormat
}
My second type:
export type Block = {
id: string
type: keyof ApiFormats
}
And the result I would like to have:
export type EditBlock = {
block: Block
apiFormat: ApiFormats[Block.type]
}
Basically, I want my the value of apiFormat
to be change based on the value of block.type
, I doubt it's unrealisable but I'm stuck on it.
Upvotes: 1
Views: 116
Reputation: 327634
In order for this to work, you need EditBlock
to be a union of block
/apiFormat
pairs for each key of ApiFormats
. First, let's allow Block
to have a specific type
property corresponding to a particular key from ApiFormats
, by making it generic:
export type Block<K extends keyof ApiFormats> = {
id: string
type: K
}
Then you want EditBlock
to be the following type:
type EditBlock = {
block: Block<"text">;
apiFormat: TextApiFormat;
} | {
block: Block<"image">;
apiFormat: ImageApiFormat;
}
This meets your criterion; if you have an EditBlock
and the block
property is Block<"text">
, then the apiFormat
property must be TextApiFormat
. And similarly for Block<"image">
.
Note that this type is unfortunately not considered a discriminated union by the TypeScript compiler, so you might have a hard time narrowing by checking editBlock.block.type
; there is currently no support for "nested" discriminated unions as requested by microsoft/TypeScript#18758. But this is the type you asked for.
It is possible to make the compiler compute EditBlock
as a function of ApiFormats
so that if you modify ApiFormats
then EditBlock
will automatically update itself. One way to do it is to write EditBlock
as a distributive object type as coined in microsoft/TypeScript#47109:
type EditBlock = { [K in keyof ApiFormats]:
{ block: Block<K>, apiFormat: ApiFormats[K] }
}[keyof ApiFormats]
Here we are first computing a mapped type with keys text
and image
(from keyof ApiFormats
) whose property value types are the relevant block
/apiFormat
object types. And then we immediately index into that mapped type with keyof ApiFormats
to get a union of the property types. You can verify that this is the same type as written out manually above.
Upvotes: 1