-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: Interface extend generic type #2225
Comments
I think it is actually quite common in JS to take an object, add properties or functions to it and return it. And I wouldn't know how else to define the return as a generic except
I've just written a definition file for such a similar case and had to discribe in the comments the workaround to actually create an interface yourself which merges
Or did I miss any other way to implement this? |
+1! I think this is the best way to use TypeScript with Immutable js records. Also generally when you want to use the decorator pattern on generic types, such as with Radium.enhancer(). |
Here's another example: interface Animal {
numberOfLegs: number;
}
interface MaleAnimal<A extends Animal> extends A {}
interface FemaleAnimal<A extends Animal, M extends MaleAnimal<A>> extends A {
mate(father:M): Promise<A[]>;
} This has two errors:
|
Why not use intersection types to model this? e.g. interface Controller {
name: string;
}
interface EventHandler {
handel(event: string): void;
}
var MyEventHandelingControler: Controller & EventHandler;
MyEventHandelingControler.name;
MyEventHandelingControler.handel("event"); |
Is this a suggestion his it could be implemented, or already a working feature? |
@pgrm, intersection types is part of TypeScript 1.6, here is the release blog |
Will I be able to do something like this: function addMetadata<T>(data: T): T & MetaData {
....
} |
@ronzeidman yes, that should work. |
Yes the example seems to suggest that it should work also with generics:
That solves what I wanted. So from my side this issue could be closed. thx @mhegazy |
@andreasbotsikas, any other scenarios not captured in the previous discussion? |
Yes and I like the fact that I transfer the logic to the function instead of declaring an interface that combines the 2 interfaces. My sample works with the following: interface IHttpPromiseInfo{
$resolved: boolean;
$error: boolean;
}
interface IHttpPost {
<T extends {}>(actionUrl: string, postData: any): IHttpPromiseInfo & T;
} Thanks for bringing this feature! |
While certain use-cases have been addressed, I'm not sure that this should be closed. |
@DanielRosenwasser I agree. For example: interface Animal {
numberOfLegs: number;
}
interface Male {
yChromosome: any;
}
interface Female<A> extends A { //Error: An interface may only extend a class or another interface.
mate(male:Male & A): (A & (Male | Female<A>))[];
} Still gets an error of: |
This would be a useful feature for working with OData GET responses. http://www.odata.org/ > Step 2. Requesting an individual resource OData mixes its own fields with a single response's fields. For example, given the following interface... interface IPerson {
FirstName: string;
LastName: string;
} ...an OData response for a single entity would be... // Shortened for brevity
{
"@odata.context":"http://my-api#People/$entity(0000-0000-0000-0000)",
"FirstName":"Russell",
"LastName":"Whyte"
} Right now, the best solutions for typing that response in a .d.ts would be having an interface IODataResponse {
"@odata.context": string
}
// Yuck!
interface IPersonResponse extends IPerson, IODataResponse { } // Less yuck
var response: IPerson & IODataResponse; A much easier (and more elegant IMO) solution would be to have IODataResponse template a generic T and extend from that T: interface IODataResponse<T> extends T { }
var response: IODataResponse<IPerson>; |
@JoshuaKGoldberg Good real world example. |
I think I found a solution for this: export type Instance<TInstance extends {}> = TInstance & {
save(fn: (err: Error) => void): void;
}; This can be used like: find(fn: (err: Error, result: Instance<IAnimal>) => void): void;
find(function(err, animal) {
animal.save((err) => void 0) // no problem
}); Still not optimal, but better. |
+1 |
2 similar comments
+1 |
+1 |
@louy GREAT!!! |
Another case where this would help ergonomics is when coupling it with classes. For example, imagine an ORM: class Model<Attributes> {
constructor(attributes:Attributes);
// ...
}
interface Model<Attributes> extends Attributes {}
// ---
interface UserAttributes {
id:number;
name:string;
}
class User extends Model<UserAttributes> {
firstName() {
return this.name.split(/\s+/)[0]; // `name` should be available.
}
} |
Just came across a need for this when declaring a base entity class as part of a setup to work with Azure Table Storage. I want to create a base entity class that absorbs all the properties of the data passed into it. That base class has a generic parameter of the data it receives. So ultimately that generic parameter is what needs to be |
@louy Except that it cannot be implemented by a class: class Foo implements Instance<Bar> {...} [ts] A class may only implement another class or interface. |
@andraaspar yes that's why it's not optimal. it's a type not an interface. |
i think this can be done in a clean way with |
I have a case where a method converts an removes some properties of a base class, which should also work on interfaces inherited from that class. The inherited interfaces would then change the base class they're inherited from: interface A {a: string}
interface AA extends A {b: string}
interface AB extends A {b_conv: string}
// Converts any type that inherits AA to the same type inheriting AB
const converter = <T extends AA>(x: T): Pick<T, Exclude<keyof T, "b">> & AB => <any>x
// Would really like this to be an interface extending T, not just an intersection type (although admittedly, this is awesome anyways, but that's not really the point here)
type X<T extends A = AA> = T & {yoho: number}
const before: X = {a: "a", b: "b", yoho: 10}
const after: X<AB> = converter(before)
const awesomeString = `${after.a} ${after.b_conv} ${after.yoho}` Having the X type a first class interface would make this even cooler, although I'm just amazed by the new advanced types in TS lately. It's a new world of type programming! |
yes, please. |
This is exactly my use-case (minus the precise example objects ;)) and my problem. +1 For this feature. |
That will resolve my issues with : https://github.com/bterlson/strict-event-emitter-types I will be able then to pass the T with Events to the StrictEventEmitter. |
I'm not sure if this is still an open issue, but I found this after receiving the error when you make an interface extend a generic. In my case, I was trying to wrap Database results in a type which enumerates the extra keys added by the mysql library. What I ended up doing isn't in the discussion yet, so I thought I'd list it. In my models file, I added export interface DBInfo {
insertId?: number;
affectedRows?: number;
changedRows?: number;
}
export type DBResponse<T> = DBInfo & T; then, in other files, I can list the return type as |
I use this small variant: type Props<T = {}> = T & {
id: string
}
type MyProps = {
message: string
}
// Usage
class MyComponent {
public props: Props<MyProps> = {
id: 'ef6872a'
message: 'Hola!'
}
} |
Since, the |
I would like to know more how interfaces extending generics is different and why is it more difficult than intersection?
|
The issue with the |
I think this feature is well-addressed by intersections. |
@RyanCavanaugh This feature is not adequately served by intersections, as @lazarljubenovic and others have pointed out in this thread, because |
@dannymcgee I went through pretty much every example in this thread and confirmed that available alternates work, e.g. this is legal now (it didn't use to be) type BaseItem<T> = T & {
title: string
help?: string
}
class M implements BaseItem<{x: string}> {
x: string;
title: string;
help: string;
} The OP is from 2015 and many interim comments refer scenarios that now work transparently or with minimal changes. I think a new issue that clearly outlines whatever shortcomings exist about current patterns and what would upsides could exist from |
Hi all,
I was hoping that typescript could support interface extending generic types like the following example:
The use case is that an http request is initiated and the IHttpPost method returns an object which gets populated when the request completes. In angular this pattern is being used by the $resource service (see definitelyTyped IResource declaration here).
Is this feasible?
Thanks in advance,
Andreas
PS: I have seen this issue in codeplex but couldn't find it here.
The text was updated successfully, but these errors were encountered: