在 Typescript 中,我可以创建强制需要属性的映射类型吗?

Seb*_*eth 5 typescript mapped-types

在 Typescript 中,我们有映射类型,可以将属性从一种类型映射到另一种类型。

一个常见的示例是 Partial 类型,它使泛型类型上的所有属性都成为“可选”。

我想实现相反的效果,比如“必需”,其中所有属性都被设置为“必需”,而不管它们预先的可选性如何。

这样的事可能吗?

(既然知道我的问题很肤浅,请参阅更新!)

更新:

一些背景...

我正在学习映射类型,并受到此处文档的启发

type Proxy<T> = {
    get(): T;
    set(value: T): void;
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
   // ... wrap proxies ...
}
let proxyProps = proxify(props);
Run Code Online (Sandbox Code Playgroud)

我心里想,嗯,“我想知道我们能用这个来搞什么恶作剧?”

所以我写了类似的东西:

type ProxyObservable<T> = Observable<T> & {
  [P in keyof T]: ProxyObservable<T[P]>;
};
Run Code Online (Sandbox Code Playgroud)

这很酷,因为你可以给它一个这样的类型:

  interface Foo {
    myNum: number;
    myString: string;
    myObject: {
      someValue: number;
      anotherValue: Date;
    };
  }
Run Code Online (Sandbox Code Playgroud)

你会得到一个ProxyObservable<Foo>类型,其中每个属性都是相同类型的 Observable,而且还具有与子属性(也是 Observable)匹配的导航属性。

const foo: ProxyObservable<Foo> = Proxify<Foo>(...)
foo.myNum.subscribe(...)
foo.myObject.subscribe(...)
foo.myObject.someValue.subscribe(...)
Run Code Online (Sandbox Code Playgroud)

邪恶,我知道。但很酷。

当您将属性(例如 myObject)设为可选时,有趣的问题就出现了。然后 Typescript 将中断:

foo.myObject.someValue.subscribe(...)
Run Code Online (Sandbox Code Playgroud)

因为它无法辨别与 Observable 联合的类型是“未定义”还是我们定义的对象。这就是我的问题出现的地方。

但是...我现在还发现这不是“可选”属性的问题,而是一般而言受歧视联合类型的问题。

我意识到,给定正确的上下文,Typescript 或许能够推断出foo.myObject属性的类型。我尝试了一些变化:

foo.myObject!.someValue //<- Nope
foo.myObject ? foo.myObject.someValue : ... //<- Nope
if(foo.myObject) foo.myObject.someValue //<- Nope
Run Code Online (Sandbox Code Playgroud)

在我看来,Typescript 编译器应该能够推断出这一点!

谁能告诉我为什么不呢?

Jus*_*oel 6

现在这是可能的,并作为 TypeScript 2.8 的类型内置到 TypeScript 中Required。

http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html

这是它的定义:

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};
Run Code Online (Sandbox Code Playgroud)


Mei*_*hes 2

不幸的是,我认为你不能用类型映射来一般地做到这一点。(很高兴被证明是错误的,如果是的话我会删除这个答案)。github 上也有这个问题的问题,但一些建议的解决方案对我不起作用。

为了一般地做到这一点,我认为您需要类型减法,这仍在讨论中。您可以使用减法undefined从 union 类型中“删除” T | undefined。

我知道“强制”某些属性存在的唯一方法是在部分内容的后面使用具有明确已知属性的联合:

interface Foo {
  a: number;
}

let a: Partial<Foo> & { a: number } = {}
Run Code Online (Sandbox Code Playgroud)

类型“{}”不可分配给类型“{ a: number;” }'。类型“{}”中缺少属性“a”。

一个例子:

class Person {
  id: string;
  name: string = "John";
  address: string = "empty";

  constructor(init: Partial<Person> & { id: string }) {
    Object.assign(this, init);
  }
}   

let person = new Person({ id: "1234" });
Run Code Online (Sandbox Code Playgroud)

你强制 id 在那里,但其他一切都是部分的。