Using this example:
interface A {
build(): SomeType;
}
interface B extends A{
build(): SomeOtherType;
}
The compiler will thrown an error that SomeOtherType is not assignable to SomeType. However, if I am holding an object of type B and I call build() on that object, I know that I will receive a value of type SomeOtherType.
I've currently used any as an override, but it would nice to be able to override the return type of functions on a parent class or interface.
But then B wouldn't actually be a subtype of A. It sounds like you just want code reuse for other inherited methods. Couldn't you just refactor your logic from A out to another class that A and B both extend from, and have each provide their own build method?
Very true, and that's usually the first route I would take. I've been hoping to avoid that though. Lately, my free time has been spent writing type definitions for DefinitelyTyped. Since the projects I'm defining interfaces for aren't my own, I was hoping to keep the types as strict to the project as possible.
That aside, the reason I wanted to suggest this was to point out an example where a more JS approach to objects and inheritance had value. Having types and laying strict constraints on inheritance is really nice, especially for someone who grew up in Java. However, the design philosophy for JS is more free form and there are a few liberties that it provides that become missed when I more strict type design is taken.
On that note though, I do want to say that I'm mostly anxious to suggest ideas because I think TypeScript is awesome and I really appreciate the time that all of you take to post back to suggestions like these, even when it's just an anxious young programmer who often times comes off like he can't be wrong.
_However, if I am holding an object of type B and I call build() on that object, I know that I will receive a value of type SomeOtherType._
But when you are holding an object of type B through a value of type A then what?
interface A {
build(): number;
}
interface B extends A{
build(): string;
}
var a: A;
var b: B;
a = b;
var result = a.build();
var aNumberOrACrash = result.toFixed()
The point of interfaces is really to verify contracts and prevent situations like this. It sounds like you're more interested in using interfaces to mix in existing functionality for code re-use (although ideally you'd also be able to mix in an actual implementation). We've previously held off on this kind of feature because it has been proposed for ES7 (or later) and we're hesitant to implement things which may later conflict with the ES standard. There isn't really a canonical thread for discussion on there but if you search the issue tracker for 'mixin' you'll find a few different threads where we've talked about options: https://github.com/Microsoft/TypeScript/search?q=mixin&type=Issues&utf8=%E2%9C%93
Thanks for the feedback though, keep it coming :)
Thank you for the info.
Wrapper module usually just override one (or some) method of the original module. How should I write .d.ts for it if not to modify the .d.ts file of the original module?
We would find it really useful to be able to do this also. In a number of places we are using a third party typing for some interface that declares one of it's sub properties or methods as as type _any_ and we would like to create more strongly typed interfaces based on these. For example the angular.ui.IStateService interface has a property called current which in turn has a property called data for storing arbitrary ....wait for it ... data (shocking I know). It would be great if we could create typings that more strongly type the data property so that we have typing in the state controller. We could get the same effect by declaring an object of a custom type and setting it to the value of $state.current.data but this is more code.
EDIT
I think I've changed my mind, I agree that it would be much better implemented as mix ins of some kind.
But when you are holding an object of type B through a value of type A then what?
@ttowncompiled why not throw a compiler error if an overload has the same parameters with different return type? I think this is what C++/Java compilers do, because overload is only possible for different types/number of parameters(otherwise it would be impossible for the compiler to determine which method to call).
To argument in favor of implementing this feature, I will describe a use case of some real world libraries that would benefit from overloading in subinterface: levelup and its extension sublevel.
Levelup batch method first argument is an object with this interface:
interface Batch {
type: string;
key: any;
value?: any;
keyEncoding?: string;
valueEncoding?: string;
}
The sublevel library usage is something like this:
import levelup = require('levelup');
import sublevel = require('level-sublevel');
var db = sublevel(levelup('/path/to/db'));
The db interface is similar to the one returned by the levelup function, but the first argument to the batch method can also contain an additional prefix field. At the moment, this is the DefinitelyTyped declaration file for sublevel:
/// <reference types="levelup" />
interface Hook {
(ch: any, add: (op: Batch|boolean) => void): void;
}
interface Batch {
prefix?: Sublevel;
}
interface Sublevel extends LevelUp {
sublevel(key: string): Sublevel;
pre(hook: Hook): Function;
}
declare module "level-sublevel" {
function sublevel(levelup: LevelUp): Sublevel;
export = sublevel;
}
The interesting part is that the Sublevel interface extends LevelUp to contain a couple of additional methods, but the Batch interface used in LevelUp is augmented with an additional property instead of being extended, which would better represent the relationship. Ideally it would look like this:
interface SubBatch extends Batch {
prefix?: Sublevel;
}
But since it is not possible for Sublevel to overload the batch method to accept a SubBatch argument, the declaration file has to augment the existing type.
The problem with this approach is that the LevelUp.batch interface also accepts the augmented Batch interface, which only makes sense in the context of Sublevel.
@danquirk I think this was prematurely declined. Here's an example of where this might be extremely useful:
import { SomeComponent, SomeComponentProps } from "./SomeComponent";
interface BlahProps {
// SomeComponentProps has an onChange method with a different signature.
onChange: (someTest: string) => void,
}
class Blah extends React.Component<BlahProps & SomeComponentProps, any> {
handleChildChange = (e: any) => {
// do something with the event child event
}
render() {
let { onChange, ...other } = this.props;
return <SomeComponent onChange={this.handleChildChange} {...other} />;
}
}
Whether you use a union type or extend the type, the onChange property becomes totally unusable in this case, so the only option is to leave onChange ignored, and create a different named property like onCustomChange or something.
There will never be anything accessing through the base type SomeComponentProps, it's purely there to allow you to pass properties through to a child component, and if you override a property then its probably thought through and dealt with accordingly.
Perhaps the best solution could be a way to create a new type that omits some properties from an existing interface?
interface A {
t1: string;
t2: number;
other: object;
}
type B = A without t1, other;
interface C extends B {
t3: object;
}
// result: C has only t2, t3.
@caesay see #4183 for that
If you really want to modify a property on some interface you can workaround by producing intermediate type that will weaken desired property to be modified:
export interface A {
prop1: string;
prop2: number;
}
// This is intermediate type and can be kept private
interface AWeak extends A {
prop1: any
}
export interface ANew extends AWeak {
prop1: boolean; // No errors here as `prop1` is already of type `any`
}
_EDIT_: I just played a little bit with this and created a mapped type that will allow you to skip extra type creation in order to override desired prop, I called it Weaken because that's what it does - makes specified properties as any:
type Weaken<T, K extends keyof T> = {
[P in keyof T]: P extends K ? any : T[P];
};
And then you can simply:
export interface A {
prop1: string;
prop2: number;
}
export interface ANew extends Weaken<A, 'prop1'> {
prop1: boolean; // No errors here as `prop1` is weakened to type `any`
}
What do you think should this helper type be added to Typescript's default definitions library?
Most helpful comment
If you really want to modify a property on some interface you can workaround by producing intermediate type that will weaken desired property to be modified:
_EDIT_: I just played a little bit with this and created a mapped type that will allow you to skip extra type creation in order to override desired prop, I called it
Weakenbecause that's what it does - makes specified properties asany:And then you can simply:
What do you think should this helper type be added to Typescript's default definitions library?