【问题标题】:Typescript: Create an interface or the narrowest type from an ObjectTypescript:从对象创建接口或最窄的类型
【发布时间】:2020-05-22 13:02:19
【问题描述】:

有没有办法从对象创建接口或窄类型?

例如,我想要类似的东西:

const obj : someType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

type myType = narrowTypeOf obj

myType 应该会导致:

{
  a: string,
  b: string,
  c: {
    c: string
  }
};

遗憾的是(或者更幸运的是)type mytype = typeof obj 中的构建将导致 someType,因为我已经声明它属于那种类型。

如果有办法将对象用作类型定义的参数 - 那也可以解决我的问题。

【问题讨论】:

  • 只是不要把它当作它不是的东西。 typeof obj 应该可以工作。
  • 问题不在于它不是它的东西。它的铸造它。如果我将其转换为 someType = { [key: string]: string | someType } - 那不会是假的。 typeof 仍然会(严格地)返回“sometype”。作为一种解决方法,我目前根本不施放它——但这不会激发快乐!仍然非常感谢您的回答!
  • 你应该总是让 TS 推断类型,所以它应该激发快乐!
  • 你是说没有办法在推断类型已经被声明为更广泛的类型之后重新获取它,还是你认为这不是一个好主意?
  • 我只是说你不应该把它输入更广泛的内容。

标签: typescript types casting


【解决方案1】:

除非SomeType 是专门的union,其中一个成员是您想要的窄类型,否则这是行不通的。让我们先看看SomeType 这样一个联合的快乐案例,尽管显然这不是你的用例:

type SomeType = {
  a: string,
  b: string,
  c: {
    c: string
  }
} | { foo: string } | { bar: string };

这里SomeType 明确地是您想要的类型和两个不相关类型的并集。那么当你给obj赋值时,注释为SomeType

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

此时捕获的类型typeof obj就是你想要的类型:

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

MyType 类型不包含{foo: string}{bar: string}。这是因为编译器在分配时使用control flow based type analysis 来缩小联合类型(请参阅microsoft/TypeScript#8010 了解实现此功能的 PR 说明)。

但是在赋值时没有基于控制流的非联合类型变量的缩小。在某一时刻,TS 团队似乎是considering it,并且有一个公开的建议(参见microsoft/TypeScript#16976)来缩小非联合类型的控制流,但目前它还不是语言的一部分。

这意味着如果您的 SomeType 比您想要的宽,但不是特别包含该元素的联合,您就会被卡住。一旦您将变量注释为该宽类型,对该变量的所有赋值都会忘记有关该类型当前值的任何更具体的内容。


这里正确的做法是来注释变量的类型。您应该将变量注释为比您分配的值更广泛的类型的唯一原因是,如果您想稍后改变变量的内容以使其成为该更广泛类型的其他居民。例如,如果SomeType 是这样给出的:

interface SomeType {
  a: string | number;
  b: unknown;
  c: object;
}

那么您将以下声明注释为SomeType 的唯一充分理由:

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

如果你打算稍后再做的话:

obj.a = 123;
obj.b = () => 456;
obj.c = [789];

假设您不打算需要更广泛的类型,那么您应该让编译器推断 obj 的类型。如果你担心这会让你用SomeType 分配一些不兼容的东西,那么你可以使用一个类似于缩小注释的辅助函数:

const annotateAs = <T>() => <U extends T>(u: U) => u;

这个辅助函数可以这样使用:

const asSomeType = annotateAs<SomeType>();

现在asSomeType() 是一个函数,它将返回其输入而不扩大,但如果该输入无法分配给SomeType,则会出错:

const oops = asSomeType({
  a: "a",
  b: "b",
  c: "c" // error! string is not an object
})

现在您可以像这样编写obj 赋值:

const obj = asSomeType({
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
});

成功了,所以obj肯定是SomeType,但是现在obj的类型是:

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

如你所愿。


好的,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-20
    • 2021-08-02
    • 2021-07-31
    • 2019-11-26
    • 2021-06-08
    • 1970-01-01
    • 2021-09-09
    相关资源
    最近更新 更多