Repository navigation
Suggestion: optional interface #371
Description
Activity
This is an alternative syntax suggestion for #340
But still 👍
Admittedly this way we do not break existing code.
Another use case is making mixins easier
Reacted by 初绎RyanCavanaugh commented
on Aug 6, 2014 MemberMore actionsMajor question I have here is that an important use case for mix-ins is that you would be able to add one of these optional interfaces after the fact, which doesn't work well with having to declare this on the original class declaration. A solution for this JS pattern should be able to handle both cases.
vvakame commented
on Aug 7, 2014 ContributorAuthorMore actionsSorry English is not my first language.
Do you sayHow to mix-in interface to already defined class??I think we should expand
TypeReferencegrammer.
TypeName [no LineTerminator here] TypeArgumentsopttoTypeName [no LineTerminator here] TypeArgumentsopt ImplementsClauseopt.use case.
module Lib { export interface IListener { on(data: any): void; } export function injectInto<T>(target: T): T implements IListener { var modified = <T implements IListener?> target; modified.on = () => { /* do something... */ }; return modified; // IListener? has compatiblity to IListener. I think this is programmer's duty. } export function listen(receiver: IListener) { receiver.on("foobar"); } } var receiver = Lib.injectInto({ name: "vvakame" }); // typeof receiver === ({name: string;} implements IListener) // I want to get expanded object type literal like {name: string; on(data: any): void;} on error message. receiver.on("foobar"); receiver.name; Lib.listen(receiver);- added a commit that references this issue
on Aug 16, 2014 +1 to something that can fix the current TypeScript mixins issues. Things like ReactJS using mixins widely and now it is very hard to make TypeScript compiler check all this things property without some effort.
For example, there are more than 400 components in my project and it will be a little bit crazy to repeat React's method defs inside every component. On the other hand i want to type-check my code. So i find out to define classes like this:
class LinkComponent extends ReactComponentBase<LinkComponentOptions,{}> implements React.ReactComponentSpec<LinkComponentOptions,{}> { // extends Component<LinkComponentOptions,{}> for instance methods like setState // implements React.ReactComponentSpec<LinkComponentOptions,{}> for type check livetime methods } // this is an `empty` class to satisfy the compiler export class ReactComponentBase<P, S> implements React.ReactComponent<P, S> { state: S; props: P; refs: { [ref: string]: React.ReactComponent<any, any>; }; getDOMNode: () => Element; setProps: (nextProps: P, callback?: () => void) => void; replaceProps: (nextProps: P, callback?: () => void) => void; transferPropsTo: <C extends React.ReactComponent<P, any>>(target: C) => C; setState: (nextState: S, callback?: () => void) => void; replaceState: (nextState: S, callback?: () => void) => void; forceUpdate: (callback?: () => void) => void; }
This leads to emty class
ReactComponentBasethat includes to every component (in compiled js code) and looks very ugly. And for my project's react mixins i need to create additional (empty) classes that extends ReactComponentBase and provide correct mixin's methods.export class MixinsComponent<P, S> extends ReactComponentBase<P, S> { // example of mixin method onValue: (bus: Bacon.Bus<any>, foo: (val: any) => void) => void; }
I think that implementation of normal mixins and unit types will make TypeScript much more friendly to such kind of libs.
Dojo is another example of a big library which could benefit greatly from TypeScript but also makes extensive use of mixins. It is not uncommon for classes in an application to require the use of 3 extra mixins. Using Dojo with TypeScript isn't really practical at the moment.
Just chiming in that this would be really useful. I had requested something like this on CodePlex last September.
I love this suggestion because it is also useful for creating
UniversalElement(#3304).Is this need met by Intersection Types #3622 (committed)? See also Feature: type alternatives (to facilitate traits/mixins/type decorators) #727, aiming at the same target.
Syntax:
A & Bis a type which implements bothAandB. (In other words roughly equivalent tointerface A_n_B implements A, B{}) It can be used wherever union types can be used.Synopsis:
interface Emitter{ on(event, fn); once(event, fn); off(event, fn); emit(event); } function MakeEmitter<T>(o T: ( T & Emitter) { require('my-emitter-library').mixin(o); return <T & Emitter>o; }Or you could provide a .d.ts which typed the function
mixinas returning the appropriate intersection type.How about this use case?
I can't find a way to do this:interface Foo { foo: string; } interface Boo extends Foo? { boo: string; } let x: Boo = getBoo(); if (x.foo) { let y: Foo = x; ... }
Essence: I want to put make
fooand optional property inBoo, but still able to typeyproperly.Here is an example that show current error when this is missing:
interface Config<S> { initialize<S>(...); reduce<S>(state: S, action: any): S; ... } class Foo<S> { configs: Config<S>[] = []; reducers: Config<S>[] = []; // <-- Could have been Reducer<S>[]; addConfig(config: Config<S>) { this.configs.push(config); if (config.reduce) { this.reducers.push(config); } } do(state: S, action: any) { return this.reducers.reduce((s, reducer) => { // Object is possibly 'undefined' (with `strictNullCheck`) return reducer.reduce(s, action); }, state); } }
Currently a workaround is to duplicate the signature of
reduce()ininterface Reducer<S> { }and make it not optional.It's also useful for
optionsparameters or strictsmergetypechecks.A very common pattern is to define default options object with values for all required properties
and when the user provide his options, do something likeObject.assign({}, defaults, userOpts).It would be nice to internally use
IOptionswith some required properties, and expose the API to the user withIOptions?(all properties optional).This would also be useful in redux when Object.assign is used as an immutable
merge.aluanhaddad commented
on Oct 24, 2016 ContributorMore actionsThis kind of type operator would be extremely useful in many cases as discussed above. One that occurs to me and I don't think it's been mentioned, is simply sending and receiving isomorphic data from a JSON service.
Generally when you do this you want anothing interface with optional properties to compose requests and the exact same interface except with required properties to type responses.
function getUser(id: number): Promise<User>; function updateUser(user: User?): Promise<void>;
It would be especially powerful when mixed with subset types and intersection types.
I don't think it should be limited to interfaces I think it should apply to type aliases.
RyanCavanaugh commented
on Jul 21, 2020 MemberMore actionsThis is now
class Bar { // Bar does not have a foo property. } interface Bar extends Partial<IFoo> { } new Bar().foo;
I think this is useful syntax :)
sample.
How useful.
In real world.
case 1.
https://github.com/borisyankov/DefinitelyTyped/blob/705102df2b652bdbadad23561675116a26e48760/space-pen/space-pen.d.ts#L52
case 2.
// Emissary#emitter
https://github.com/atom/emissary#emitter
// .d.ts
https://github.com/borisyankov/DefinitelyTyped/blob/b4aba562ef4192bc3f2e770ab2018f89e047836b/emissary/emissary.d.ts#L13
// usage in TypeScript (dirty hack!)
https://github.com/vvakame/language-review/blob/d23f8797bbb6e99d636399a14c7035c4809ab365/lib/util/emissary-helper.ts#L11