I have the following setup, where I want to use TypeScript to make sure the user is only choosing types that are allowed in the interface.
This works fine if I call the class directly, but if I use a custom generic function to pass back same class function it does not work and gives an error.
Can anyone help me understand how to fix this?
export interface IMyInterface {
test1: {
test1Sub: {
test1SubSub: string
}
},
test2: {
test2Sub: {
test2SubSub: string
}
}
}
export default class MyClass<T> {
parentFunction = <A extends keyof T>(app: A) => (
{
childFunction: (page: keyof T[A]) => {
console.log(app, page)
}
}
)
}
// simplified for example
export function useMyCustomHook<T> (app: keyof T) {
const settings = new MyClass<T>();
return settings.parentFunction(app)
}
// Works when calling class directly
const myClass = new MyClass<IMyInterface>()
const parent = myClass.parentFunction('test1')
const child = parent.childFunction('test1Sub')
// does not work when calling via hook
const myHookValue = useMyCustomHook<IMyInterface>('test1')
myHookValue.childFunction('test1') // ERROR - Argument of type 'string' is not assignable to parameter of type 'never'.
I have created a playground example of this.
CodePudding user response:
You just need an explicit type for the app parameter:
export function useMyCustomHook<T, A extends keyof T>(app: A) {
const settings = new MyClass<T>();
return settings.parentFunction(app);
}
const myHookValue = useMyCustomHook<IMyInterface, "test2">("test2");
myHookValue.childFunction('test2Sub')
This is because without extending keyof T
, inside the scope of useCustomHook
the parameter app
will always be treated as the widest type keyof T
. When you use it with childFunction
that creates a function signature of:
childFunction: (page: keyof T[keyof T]) => {
console.log(app, page)
}
The type keyof T[keyof T]
on MyInterface
resolves to an intersection of two string literal types "test1Sub"
and "test2Sub"
, which is always never
.