jsx で何の気なしにclassName
属性を使っている時の型はReact.HTMLAttributes
の中にclassName?: string
と定義(@types/react@16.9.2
では@types/react/index.d.ts:1628
辺り)されています。
また例えば<div>
タグを使った時の型はdiv: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
と定義(JSX.IntrinsicElements
で@types/react/index.d.ts:2858
辺り)されています。
ということで、実質React.HTMLAttributes
が使われているということで、それを自分で上書きしてあげれば良さそうに感じますがdelare module 'react'
で定義したとしても既存のプロパティの解決はデフォルトのもの(@types/react
)が優先して使われるようなので無理です。
ではどうするかと言うと、JSX.IntrinsicElements
からすべて再定義してあげます。まずは既存のJSX.IntrinsicElements
をすべてコピーしdelare module 'react'
内に置きます。
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
a: React.DetailedHTMLProps<
React.AnchorHTMLAttributes,
HTMLAnchorElement
>;
}
}
}
あとはこのReact.AnchorHTMLAttributes
を更に再定義してあげてあげれば目的は果たせそうです。ですがReact.AnchorHTMLAttributes
はa
の場合だけで、table
だったらReact.TableHTMLAttributes
、input
だったらReact.InputHTMLAttributes
だったりと数が多くすべてに対応していたらそれだけで一苦労です。
TypeScript には、
interface A {
foo?: string;
}
interface B {
foo: 'foo'
}
type C = A & B;
// C = {foo: 'foo'};
になるという法則があります。そしてすべてのReact.*HTMLAttributes
はReact.HTMLAttributes
を拡張したものなので、React.HTMLAttributes
を継承したものをジェネリクス型として受け取れる何か型を定義し、{className: 自分の型;}
とすればいけそうです。
declare module 'react'
の中にこのようなものも定義してみました。
type MyHTMLAttributes = T & {
className?: 'foo' | 'bar';
}
やっていることは前のコードと同じstring | undefined
を'foo' | 'bar'
へ変換することです。あとはすべてのReact.*HTMLAttributes
をMyHTMLAttributes
で囲めば目的が果たせそうです。
VSCode 以外はできるか分かりませんが、できるなら以下の組み合わせで一気に変換すると楽かなと思います。
\s(React.*HTMLAttributes(?!>)<[^,]*)
# から
MyHTMLAttributes<$1>
# 先頭にスペース注意