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 }