I'm working on a project that has the following pattern:
interface IAuthProvider {
middleware: () => void
getInput: () => void
}
class Password implements IAuthProvider {
middleware = () => {}
getInput = () => {}
}
class Email implements IAuthProvider {
middleware = () => {}
getInput = () => {}
}
const providerNames = ['Email', 'Password']
type AuthProviderNamespace = { [K in typeof providerNames[number]]: IAuthProvider }
// error: Type 'typeof Password' is missing the following properties from type 'IAuthProvider': middleware, getInput
const providers: AuthProviderNamespace = { Email, Password }
I need to use AuthProviderNamespace because this kind of object will be imported in a module and passed directly to a function that will iterate over it:
import * as AuthProviders from './providers/index.ts'
const providers = instantiateProviders(AuthProviders)
And so I need to type AuthProviders. But I'm doing something wrong such that typescript doesn't recognize Email and Password as implementers of IAuthProvider. Can I fix this?
Playground: https://tsplay.dev/wQV7jN
CodePudding user response:
The error is correct. The value {Email: Email, Password: Password} is an object whose properties are each constructors of IAuthProvider objects. But the type {Email: IAuthProvider, Password: IAuthProvider} corresponds to an object whose Email and Password properties are IAuthProvider instances, not constructors of such instances. The class constructor Email does not have a getInput property, so it cannot be an IAuthProvider:
Email.getInput; // <-- error, Property 'getInput' does not exist on type 'typeof Email'
Assuming your intent with providers is to use its properties to construct new instances of IAuthProvider, like this:
const provider = new providers.Email(); // note, assuming a no-arg constructor
provider.getInput(); // okay
provider.middleware(); // okay
Then you need to change the AuthProviderNamespace type so that its properties are no-arg constructors of IAuthProvider instances. You can use a construct signature like { new(): IAuthProvider } or like new () => IAuthProvider:
type AuthProviderNamespace = {
[K in typeof providerNames[number]]: new () => IAuthProvider
}
And then everything works as desired:
const providers: AuthProviderNamespace = { Email, Password }
