Is there a way to write a Typescript definition for an ES6 mix-in?
I've this pattern in library.js
, and I'd like to create the library.d.ts
// declaration in `library.js`
class Super extends Simple {
constructor() {}
static Compose(Base = Super) {
return class extends Base {
// ...
}
}
}
// usage in `client.js`
class MyClass extends Super.Compose() {}
let myInstance = new MyClass();
class MyOtherClass extends Super.Compose(AnotherClass) {}
No, Typescript type system is not expressive enough for that - see the discussion in https://github.com/Microsoft/TypeScript/issues/7225 and https://github.com/Microsoft/TypeScript/issues/4890.
The idiomatic 'type of classes' in typescript is written as
interface Constructor<T> {
new (...args): T;
}
So one way to write declaration for Compose is
export declare class Simple {}
export declare class Super extends Simple {
static Compose<T>(Base?: Constructor<T>): Constructor<T & {/*mixed-in declarations*/}>
}
That is, Compose return type is declared to be a constructor for intersection type - a type which must have all the properties of parameter (Base) together with all the properties of the mixin.
You can use that declaration (assuming it's in the library.d.ts file) like this
import {Super} from './library'
let MyComposed = Super.Compose(Super)
let myInstance = new MyComposed
The minor inconvenience is that you always have to provide argument for Super.Compose() because type inference does not work without knowing the value for default parameter, and you can't provide value for default parameter in the declaration file.
But the big problem is that you can't really use the result of Compose as a class:
class MyClass extends Super.Compose(Super) {}
does not compile due to the issues mentioned above:
error TS2509: Base constructor return type 'Super & {}' is not a class or interface type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With