Typescript array of different generic types

后端 未结 2 967
失恋的感觉
失恋的感觉 2020-12-11 20:56

If I have an array a where each element is an object consisting of two properties first and second, how should I declare `a\'s type su

相关标签:
2条回答
  • 2020-12-11 21:22

    If you want an array where first will always have the same type, this will do

    interface IArrayGeneric<T> {
        first: T;
        second: (arg: T) => string;
    }
    
    const a: Array<IArrayGeneric<   type   >>;
    

    This will ensure that you can't put any object into a that doesn't satisfy the above requirements, but will also constrain T to one specific type you choose.

    0 讨论(0)
  • 2020-12-11 21:42

    The element type of the array would need to be an "existential type", which we could write in pseudocode as exists T. { first: T, second: (arg: T) => string }. TypeScript currently does not support existential types natively.

    One potential workaround is to encode existential types using closures as explained in this answer. If you don't want to use real closures at runtime, you can use a utility library that provides a type definition for existential types (based on an encoding of type functions using indexed access types) and functions to produce and consume existential types that perform type casts but are just the identity at runtime:

    // Library
    // (Based in part on https://bitbucket.org/espalier-spreadsheet/espalier/src/b9fef3fd739d42cacd479e50f20cb4ab7078d534/src/lib/type-funcs.ts?at=master&fileviewer=file-view-default#type-funcs.ts-23
    // with inspiration from https://github.com/gcanti/fp-ts/blob/master/HKT.md)
    
    const INVARIANT_MARKER = Symbol();
    type Invariant<T> = {
        [INVARIANT_MARKER](t: T): T
    };
    
    interface TypeFuncs<C, X> {}
    
    const FUN_MARKER = Symbol();
    type Fun<K extends keyof TypeFuncs<{}, {}>, C> = Invariant<[typeof FUN_MARKER, K, C]>;
    
    const BAD_APP_MARKER = Symbol();
    type BadApp<F, X> = Invariant<[typeof BAD_APP_MARKER, F, X]>;
    type App<F, X> = [F] extends [Fun<infer K, infer C>] ? TypeFuncs<C, X>[K] : BadApp<F, X>;
    
    const EX_MARKER = Symbol();
    type Ex<F> = Invariant<[typeof EX_MARKER, F]>;
    function makeEx<F, X>(val: App<F, X>): Ex<F> { 
        return <any>val;
    }
    function enterEx<F, R>(exVal: Ex<F>, cb: <X>(val: App<F, X>) => R): R { 
        return cb(<any>exVal);
    }
    
    // Use case
    
    const F_FirstAndSecond = Symbol();
    type F_FirstAndSecond = Fun<typeof F_FirstAndSecond, never>;
    interface TypeFuncs<C, X> { 
        [F_FirstAndSecond]: { first: X, second: (arg: X) => string };
    }
    
    let myArray: Ex<F_FirstAndSecond>[];
    myArray.push(makeEx<F_FirstAndSecond, number>({ first: 42, second: (x) => x.toString(10) }));
    myArray.push(makeEx<F_FirstAndSecond, {x: string}>({ first: {x: "hi"}, second: (x) => x.x }));
    for (let el of myArray) { 
        enterEx(el, (val) => console.log(val.second(val.first)));
    }
    

    (If there's enough interest, I may properly publish this library...)

    0 讨论(0)
提交回复
热议问题