How do I express this in Typescript?

回眸只為那壹抹淺笑 提交于 2019-12-21 07:10:09

问题


Let's say I have an interface A:

interface A {
  foo: number
  bar: string
}

And I have a generic type Option:

type Option<T> = {
  map: () => T
}

Then I create a new interface B from A and Option:

interface B {
  foo: Option<number>
  bar: Option<string>
}

How can I make this operation more general? Ie. The API I want is:

type B = Lift<A>

Where Lift automatically maps each member of A to an Option. Note that A can have any number of members, of any type.

How can I implement Lift? If this is not possible in TypeScript, does anyone have a Scala/Haskell solution?


回答1:


Good news: With TypeScript 2.1.0, this is now possible via Mapped Types:

type Option<T> = { map() => T };
type OptionsHash<T> = { [K in keyof T]: Option<T[K]> };
function optionsFor<T>(structure: T): OptionsHash<T> { ... };

let input = { foo: 5, bar: 'X' };
let output = optionsFor(input);
// output is now typed as { foo: { map: () => number }, bar: { map: () => string } }

The opposite is also possible:

function retreiveOptions<T>(hash: OptionsHash<T>): T { ... };

let optionsHash = {
    foo: { map() { return 5; } },
    bar: { map() { return 'x'; } }
};
let optionsObject = retreiveOptions(optionsHash);
// optionsObject is now typed as { foo: number, bar: string }



回答2:


You are looking for higher-kinded types. Here it is in Scala:

trait FooBar[M[_]] {
  val foo: M[Integer]
  val bar: M[String]
}

type Identity[X] = X
type A = FooBar[Identity]
type B = FooBar[Option]

You can use any second-order types e.g.:

type C = FooBar[List]

But these will not compile:

// type S = FooBar[String] ---> String is a first-order type
// type M = FooBar[Map]    ---> Map[K, V] is a third-order type

Unfortunately, this has not yet made it into TypeScript but there is an open issue for it: https://github.com/Microsoft/TypeScript/issues/1213




回答3:


I haven't looked at TypeScript for a while (I think it was around version 1.0), so I can't really tell if it's there now.

What you want requires a type system feature called higher kinded types ; it allows one to construct types by passing them as arguments to type constructors, very much like function application, lifted to the type level.

You'll have to adjust A's definition in order to make this work. Here's how I'd achieve what you want in Haskell :

-- First, I need a more general definition for A
data GeneralizedA f = A { foo :: f Int, bar :: f String }

-- So that I can re-encode the original A like this :
type A = GeneralizedA Identity

-- Guessing what the Option type would be since
-- Haskell's type system is more precise here :
data Option a = Option { optionMap :: IO a }

-- And here's the result :
type B = GeneralizedA Option


来源:https://stackoverflow.com/questions/36900619/how-do-i-express-this-in-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!