Skip to content

Generic type parameter problem/limitation/question #4967

Description

@myitcv

Consider the following which is a modified cut-down version of the immutable-js type definition:

interface IIterable<T, K, V> {
    has(key: K): boolean;
    includes(value: V): boolean;
    slice(begin?: number, end?: number): T
    // flatMap<MK, MV>(...): Problems here!
}

interface ISeq<T, K, V> extends IIterable<T, K, V> {
    toSeq(): T;
}

interface IKeyedIterable<T, K, V> extends IIterable<T, K, V> {
    // flip(): Problems here!
}

interface List<V> extends IIterable<List<V>, number, V> {
    set(index: number, value: V): List<V>;
}

interface KeyedSeq<K, V> extends ISeq<KeyedSeq<K, V>, K, V>, IKeyedIterable<KeyedSeq<K, V>, K, V> {
}

The existence of the type parameter T for the definitions of IIterable and ISeq allows slice() to reflect that it returns a sliced instance of the same container 'type' without requiring a type assertion:

let l1: List<string>;
let l2: List<string> = l1.slice(0, 2);

However I can't type flatMap and flip. Taking flip as an example, it should return a container of the same 'type' but with K and V switched. flatMap should return a container of the same 'type' but with MK and MV.

This problem would be 'solved' if we could refer to generic types without requiring that they be passed arguments:

interface IIterable<T, K, V> {
    has(key: K): boolean;
    includes(value: V): boolean;
    slice(begin?: number, end?: number): T<K, V>
    flatMap<MK, MV>(mapper: (value?: V, key?: K, iter?: T<K, V>) => T<MK, MV>, context?: any): T<MK, MV>
}

interface ISeq<T, K, V> extends IIterable<T, K, V> {
    toSeq(): T<K, V>;
}

interface IKeyedIterable<T, K, V> extends IIterable<T, K, V> {
    flip(): T<V, K>;
}

interface List<V> extends IIterable<List, number, V> {
    set(index: number, value: V): List<V>;
}

interface KeyedSeq<K, V> extends ISeq<KeyedSeq, K, V>, IKeyedIterable<KeyedSeq, K, V> {
}

Am I missing something obvious here?

Or do I need to look to some higher order definition with code generation of (generic) interfaces to properly support this?

Activity

  1. mhegazy commented on Sep 30, 2015

    @mhegazy
    Contributor

    from a cursory look, it seems that flatMap should be defined as*:

    export interface Iterable<K, V> {
        flatMap(
          mapper: (value?: V, key?: K, iter?: this) => this,
          context?: any
        ): this;
    }

    this way no need to infer any new type parameters, just pass them through, which seems to be the expected behavior of flatMap if understand the documentation correctly.

    * I am refereing to new this types from #4910

  2. mhegazy commented on Oct 21, 2015

    @mhegazy
    Contributor

    Paul Jolly (@myitcv) please reopen if this does not address the issue.

  3. myitcv commented on Nov 4, 2015

    @myitcv
    Author

    Mohamed Hegazy (@mhegazy) apologies, somehow I totally missed your reply!

    Yes #4910 does help for certain functions.

    However for map and flatMap, this can't be used as the return type because those methods are themselves generic:

    export interface Iterable<K, V> {
        map<M>(
          mapper: (value?: V, key?: K, iter?: this) => M,
          context?: any
        ): Iterable<K, M>; // can't use 'this' here
    }

    Any thoughts?

  4. sandersn commented on Nov 5, 2015

    @sandersn
    Member

    No, you're not missing something obvious. Typescript doesn't support higher-kinded types. Code generation might be the answer if you can't express the concept any other way.

    Haskell's Functor typeclass is essentially what you want, but with more methods than just map.

    The type is fmap :: Functor f => (a -> b) -> f a -> f b -- replace Functor with Iterable and you get

    function fmap<F extends Iterable, A, B>(mapper: (a:A) => B, i: F<A>): F<B> {
        // code that uses the iterable interface to iterate over i...
    }

    Unfortunately, Typescript doesn't support higher-order types, so you'll have to find a different way of expressing this code.

  5. myitcv commented on Nov 5, 2015

    @myitcv
    Author

    Nathan Shively-Sanders (@sandersn) - thanks for confirming. Code generation is probably the cleanest solution here.

  6. shelby3 commented on May 21, 2017

    @shelby3

    Unfortunately this means no Functor map nor generic iterator factories.

  7. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions