不同泛型类型的打字稿数组 [英] Typescript array of different generic types

查看:27
本文介绍了不同泛型类型的打字稿数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有一个数组 a,其中每个元素都是一个由两个属性 firstsecond 组成的对象,我应该如何声明 `a's键入以便始终满足以下条件?

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 such that the following is always satisfied?

 ForAll(x in a)(type(x.first) == T iff type(x.second) == (T => string))

即,例如,我想确保 a[3].second(a[3].first) 是类型安全的.

I.e., I want to make sure that, for example, a[3].second(a[3].first) is typesafe.

推荐答案

数组的元素类型需要是一个存在类型",我们可以用伪代码写成exists T.{ first: T,第二个:(arg:T)=>字符串}.TypeScript 目前本身不支持存在类型.

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...)

这篇关于不同泛型类型的打字稿数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆