Reputation: 745
Suggestion needed: As a newbie, I have class like below in typescript:
export declare class SampleOne extends Setting {
getValue(): Promise<boolean>;
setValue(value: boolean): Promise<void>;
}
And
export declare class SampleTwo extends Setting {
getValue(): Promise<boolean>;
setValue(value: boolean): Promise<void>;
}
Now i want a helper function where i can pass either SampleOne or SampleTwo like below:
async function getObj(title: string, categories: string, cname: Setting) {
let obj = await et.findSetting(title,categories) as cname;// i want to pass either **SammpleOne** or **SampleTwo** class.
return obj;
}
Function call would be like
getObj(title, categories, SampleOne)
getObj(title, categories, SampleTwo)
I am strangling to created this helper function.How it should be looks like in typescript??
Thanks in advance.
Upvotes: 0
Views: 316
Reputation: 25966
I would go with Convenience generic pattern
async function getObj<T extends Setting>(title: string, categories: string): Promise<T> {
let obj = await et.findSetting(title,categories) as T;
return obj;
}
or, dropping the unncecessary async:
function getObj<T extends Setting>(title: string, categories: string): Promise<T> {
return et.findSetting(title,categories) as Promise<T>;
}
Usage:
const s1 = getObj<SampleOne>('a', 'b');
Pros:
Cons:
If you need runtime check and have settings modeled as classes, you can use Type (as coded by Angular folks):
interface Type<T> extends Function { new (...args: any[]): T; }
async function getObj<T extends Setting>(
title: string, categories: string, klass: Type<T>): Promise<T>
{
let obj = await et.findSetting(title,categories);
if (klass.prototype.isPrototypeOf(obj)) {
return obj as T;
} else {
throw new Error(`Expected ${klass.name} but got ${obj.constructor.name}`)
}
}
const s1 = getObj('a', 'b', SampleOne);
s1.then(x => console.log(x))
.catch(e => console.log(e));
Upvotes: 1
Reputation: 33051
I believe you can just apply this constraint T extends typeof Setting
to generic:
class Setting {
getValue(): Promise<boolean> { return Promise.resolve(true) };
setValue(value: boolean): Promise<void> { return Promise.resolve() };
}
class SampleOne extends Setting {
getValue(): Promise<boolean> { return Promise.resolve(true) };
setValue(value: boolean): Promise<void> { return Promise.resolve() };
}
class SampleTwo extends Setting {
getValue(): Promise<boolean> { return Promise.resolve(true) };
setValue(value: boolean): Promise<void> { return Promise.resolve() };
}
class DontAllowed { }
type AllowedClasses = typeof SampleOne | typeof SampleTwo
class SampleThree { // does not extends Setting
getValue(): Promise<boolean> { return Promise.resolve(true) };
setValue(value: boolean): Promise<void> { return Promise.resolve() };
}
function getObj<T extends typeof Setting>(cname: T) {
return cname
}
const result = getObj(SampleOne) // ok
const result2 = getObj(SampleTwo) // ok
const result3 = getObj(DontAllowed) // expected error
In above example, I assumed that every class which extends Setting is allowed. If you want to allow anly two classes, you can do this thing:
type AllowedClasses = typeof SampleOne | typeof SampleTwo
function getObj<T extends AllowedClasses>(cname: T) {
return cname
}
I don't think that it is necessary to use type assertion (as
operator) in such cases.
Upvotes: 1
Reputation: 2412
Use generics.
async function getObj<T>(title: string, categories: string) {
let obj = await et.findSetting(title,categories) as T;
return obj;
}
Then you can call the function with the desired type:
getObj<SampleOne>('foo', 'bar');
getObj<SampleTwo>('foo', 'bar');
Upvotes: 0
Reputation: 214969
You can make getObj
generic by the type of Setting
and parametrize the last argument as a constructor of T:
type Class<T> = new (...args: any) => T;
async function getObj<T extends Setting>(
title: string, categories: string, klass: Class<T>): Promise<T>
{
return .... as T
}
Upvotes: 1