Reputation: 3118
I want to achieve:
on
, it's type must be a Function
boolean
My first naive approach was:
interface Props {
[key: `on${string}`]: Function; // error: `on${string}`' index type 'Function' is not assignable to 'string' index type 'boolean'.ts(2413)
[key: string]: boolean;
}
My second try was:
type EventName = `on${string}`;
interface Props {
[K: EventName | string]: typeof K extends EventName ? Function : boolean;
}
const props: Props = {
asd: true,
onClick: () => {}, // error: Type '() => void' is not assignable to type 'boolean'.ts(2322)
};
So is it possible to achieve this, maybe in a different way?
Upvotes: 1
Views: 4253
Reputation: 250366
This isn't 100% possible, since the code couldn't be fully checked. If s
contains a string
there is no real way to prevent it from containing a string
starting with on
. This is why the string
index signature needs to be compatible with all defined props.
let s = "onClick" as string;
let v = props[s] // typed as boolean, is actually onClick
One version to get around this warning (although not to make it safe) is to use an intersection instead of an interface. This will allow access to properties to work as you want them to, but creation of such objects requires a type assertion, as the underlying incompatibility is still there
type Props = {
[key: `on${string}`]: Function;
} & {
[key: string]: boolean;
}
const props: Props = {
asd: true,
onClick: () => {},
} as any as Props;
props.onClick // Function
props.ssss // boolean
For creation, to avoid the any
type assertio, and get some validation for object creation, we could use a function that validates that any keys not prefixed with on
are of type boolean:
function makeProps<T extends Record<`on${string}`, Function>>(o: T & Record<Exclude<keyof T, `on${string}`>, boolean>) {
return o as Props;
}
const props: Props = makeProps({
asd: true,
onClick: () => {},
})
The safer solution would be to make the boolean
and the Function
keys disjoint by using a prefix for the boolean
props as well.
type Props = {
[key: `on${string}`]: (...a: any[]) => any;
[key: `flag${string}`]: boolean;
}
const props: Props = {
onClick: () => {},
flagRaiseClick: true
}
props.onClick // Function
props.flagRaiseClick // boolean
Upvotes: 2