Reputation: 218
I try to modify code, that works in regular usage, to use with declarations generation (or manually write declarations). The code is like this:
function createClass<T>( data: T )
{
abstract class Internal<T2>
{
public constructor( arg: T2 )
{
console.log( data, arg );
}
protected abstract method( arg: T ): void;
}
return Internal;
}
The problem is that i should to specify type of function result, but I don't know how to do this — I tried different options and none worked.
Added:
This small example may not be completely clear, so I add the original code with which the problem occurred:
Original code, that I'm trying to turn into a package: https://gist.github.com/Avol-V/1af4748f4b0774a999311c92b6dc1631
Code of small-redux
you can find there: https://github.com/m18ru/small-redux/blob/master/src/createStore.ts
Code of preact
you can find there: https://github.com/developit/preact/blob/master/src/preact.d.ts
The problem is that I don't want to specify the TState type to every usage of created class — it can't be changed on created class so it's incorrect.
Added:
So, I didn’t find any solution to not specify T
explicitly in the generic of the created class (additionally to the T2
— class Internal<TInternal, T2>
). My result code looks like: https://github.com/m18ru/preact-small-redux/blob/85c143e851b92c44861dc976ce6ef89bcda2c884/src/index.ts
If you just want to write a function that returns class with generic, you can do this as @baryo suggests (with some cleanup):
declare abstract class AbstractInternal<T>
{
public abstract method( arg: T ): void;
}
function createClass(): typeof AbstractInternal
{
abstract class Internal<T> implements AbstractInternal<T>
{
public abstract method( arg: T ): void;
}
return Internal;
}
const A = createClass();
class B extends A<string>
{
public method( arg: string ): string
{
return 'Hello ' + arg;
}
}
Upvotes: 0
Views: 1052
Reputation: 1461
Everything you create inside the function is scoped to it, including types. So you need to at least define types out of it. Maybe the following will help you:
interface IInternal<T> {
method(arg: T): void;
}
type InternalCtor<T> = new (param: T) => IInternal<T>;
function createClass<T>(data: T): InternalCtor<T>
{
abstract class Internal<T> implements IInternal<T>
{
public constructor( arg: T )
{
console.log( data, arg );
}
public abstract method( arg: T ): void;
}
return Internal as InternalCtor<T>;
}
class A extends createClass<number>(1) {
public method() {
console.log('hello');
}
}
const z = new A(2); // 1 2
edit: you mentioned you're interested that a generic method will return a generic class - it becomes a bit tricky with typings but we can just let Typescript itself infer everything we need.
function createClass<T>(data: T)
{
// directly return a new class with a different generic type.
// notice that you can't return an abstract class.
return class Internal<U>
{
public constructor( arg: U )
{
console.log( data, arg );
}
public method(arg: T): void { console.log(arg); }
}
}
// first generic type (number) is for the function, then the second generic type (string) is for the returned class.
class A extends createClass<number>(1)<string> {
}
const z = new A('zzz'); // 1 zzz
z.method(2); // 2
edit 2: it's a bit different from what you're looking but i think a better practice will be the following (similar to what @Diullei suggested):
abstract class AbstractInternal<T> {
public abstract method(arg: T): void;
}
type InternalCtor<T> = new (param: T) => AbstractInternal<T>;
function createClass<T, U>(data: T): InternalCtor<U> {
abstract class Internal<U> implements AbstractInternal<U>
{
public constructor(arg: T) {
console.log(data, arg);
}
public abstract method(arg: U): void;
}
return Internal as any as InternalCtor<U>;
}
class A extends createClass<number, string>(1) {
public method(arg: string) { // now I need to implement this to avoid a compilation error
console.log(`hello ${arg}`);
}
}
const z = new A('arg'); // 1 "arg""
z.method('arg2'); // "hello arg2""
Upvotes: 2