Dans cette vidéo nous allons voir un cas qui donne souvent du fil à retordre quand on débute, un objet dont la forme dépend d'une de ses propriétés.
type HTMLShape = {}
const img: HTMLShape = {
tagName: 'img',
class: '.demo',
attributes: {
alt: 'demo'
}
}
const input: HTMLShape = {
tagName: 'input',
attributes: {
name: 'demo',
type: 'text'
}
}
const textarea: HTMLShape = {
tagName: 'textarea',
attributes: {
cols: 3,
name: 'text'
}
}
Le principe est de faire varier le type de attributes en fonction da la valeur du tagName.
Solution
La solution est de créer un type union qui va représenter toutes les variations possibles des formes en fonction du tagName. On commence par créer un type objet pour représenter les valeurs possibles.
type HTMLAttributes = {
img: {
alt: string
},
input: {
type?: 'text' | 'number',
name?: string
},
textarea: {
name?: string,
cols?: number,
}
}
On crée ensuite un type permettant de créer la forme attendue en fonction du tag
type HTMLShapeForTag<TagName> = {
tagName: TagName,
class?: string,
id?: string
attributes?: TagName extends keyof HTMLAttributes ? HTMLAttributes[TagName] : never
}
Ensuite on veut créer un union des cas possible, en se basant sur les clef de HTMLAttributes. On va commencer par utiliser un mapped Type
type HTMLShapes = {
[Key in keyof HTMLAttributes]: HTMLNode<Key>
}
On va le convertir en union type via un type utilitaire "ValueOf"
type ValueOf<T> = T[keyof T]
type HTMLShape = ValueOf<HTMLShapes>
Et voila le résultat sans passer par des alias intermédiaires.
type ValueOf<T> = T[keyof T]
type HTMLAttributes = {
img: {
alt: string
},
input: {
type?: 'text' | 'number',
name?: string
},
textarea: {
name?: string,
cols?: number,
}
}
type HTMLShapeInline = ValueOf<{
[TagName in keyof HTMLAttributes]: {
tagName: TagName,
class?: string,
id?: string
attributes?: HTMLAttributes[TagName]
}
}>