React擬きのJSX関数を作る時のメモです。
どういうことか
create_dom.ts(before)
export const CreateDOM = (tag: string, props: object): HTMLElement => {
const el = document.createElement(tag);
switch(tag){
case "a":
el.href = props.href;
case "p":
el.textContent = props.text;
break;
case "img":
el.src = props.src;
el.alt = props.alt;
break;
}
return el;
}
index.ts
import { CreateDOM } from "./create_dom.ts";
CreateDOM("p", {text: "aaaa"})
// ↑ここで型推論したい!!!
1.まず型を定義する
types.ts
export interface ElementProps{
p: {text: string},
a: {text: string, href: string},
img: {src: string, alt: string},
h2: {text: string}
}
2.型から空の実装(?)を作る
types.ts
export interface ElementProps{
p: {text: string},
a: {text: string, href: string},
img: {src: string, alt: string},
h2: {text: string}
}
//型を決めておく(ここ重要、XX|undefinedや?が効かなくなる。)
export const ImplementElementProps: ElementProps = {
p: {text: ""},
a: {text: "", href: ""},
img: {src: "", alt: ""},
h2: {text: ""}
}
2.プロパティ名になる部分をジェネリックにする
create_dom.ts(after)
import { type ElementProps, ImplementElementProps } from "./types.ts";
export const CreateDOM = <K extends keyof ElementProps>(tag: K, props: typeof ImplementElementProps[K]): HTMLElement => {
const el = document.createElement(tag);
switch(tag){
case "a":
el.href = props.href;
case "p":
case "h2"
el.textContent = props.text;
break;
case "img":
el.src = props.src;
el.alt = props.alt;
break;
}
return el;
}
解説(適当)
ジェネリックで前もってkeyofにして引数の型を固定しておけば型がしっかりすると思われる。
propsの部分はtypeofから変数を参照してtagをプロパティ名として使えるようにして[]でアクセスし型を取得できていると思われる。
TypeScriptの複雑な型の解決は難しいが解決出来た瞬間が非常に気持ちいいのである。Microsoftは神。