【问题标题】:How to let TypeScript know the keys of my object?如何让 TypeScript 知道我的对象的键?
【发布时间】:2021-01-04 00:03:58
【问题描述】:

我用 TypeScript 输入这个:

const k = {
  container: {
   width: '100%',
  },
}

当我使用智能感知时,TypeScript 发现我的对象 k 有一个名为 container 的属性似乎很酷。它工作正常。现在,我想限制属性的值,所以我声明了一个接口:

interface StyleSet {
   [key: string]: React.CSSProperties
}

现在我将类型添加到前一个对象,所以我们有:

const k: StyleSet = {
  container: {
   width: '100%',
  },
}

一切正常。现在智能感知可以识别,TypeScript 强制对象 k hare 类型为 React.CSSProperties 的属性值。

但是,问题来了,现在智能感知(和 TypeScript!?)不再识别我的 k 对象中的属性名称。即当我输入k. 时,它并没有建议(并且似乎忽略)我之后输入的内容。所以我可以输入 k.potatoe 并毫无问题地使用该值(当然,该值是 undefined / null )但 TypeScript 不再提示任何错误。

问题:如何告诉 TypeScript,属性的值都是 React.CSSProperties 类型,但是当我使用对象时,仍然限制属性的名称?也就是说,我只能输入k.container

我在网上搜索了大量关于 TypeScript 的文档和示例,但似乎这是不可能的。我尝试了接口、类型、记录、只读...

请注意,对象的键是未知的。例如,一个可能是:

const a: StyleSet = {
  potato:{
    width: '100%',
  },
}

而另一个对象可能是:

const b: StyleSet = {
  ketchup: {
    width: 'auto',
  },
}

另请注意,对此有部分解决方案,使用ReturnType TypeScript 可以做我想做的事,但我需要将每个对象包装在一个函数中。我也觉得这很荒谬。

欢迎任何想法:)

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    您正在创建StyleSet Index Type。如果仔细观察,它对确切的键名一无所知,它只知道,键可以是任何字符串。

    Typescript 也知道这么多信息。当您将StyleSet 分配给const k 时,您会丢失键名的信息。在代码中,打字稿不知道k 包含container 字段。

    您可以做的是,如果您知道 StyleSet 中应该可用的键的静态列表,那么您可以在类型本身中捕获它。使用索引类型,您将无法静态访问其字段。

    【讨论】:

    • 哈哈哈你完全描述了我的问题。我缺少的是我不知道如何解决它。
    • 如果您不知道静态键,那么您如何期望 typescript 知道它们?
    【解决方案2】:

    这是一种方法:

    type PossibleKeys = 'container' | 'foo' | 'bar';
    
    type StyleSet = {
        // Question mark makes them optional properties.
        // Otherwise TypeScript will require all keys in `PossibleKeys` to present.
        [key in PossibleKeys]?: React.CSSProperties
    }
    
    const k: StyleSet = {
        container: {
            width: '100%',
        },
    }
    
    // Now when you type `k.`, you will see three suggestions listed above.
    

    Playground Link 中尝试一下。

    更新

    好的,所以你真正想要的是一组动态的键,基于给定的对象字面量,你已经知道函数解决方案,但你不想使用它。但是,在目前 TypeScript 的限制下,似乎根本没有更好的解决方案,原因如下。

    首先,很明显,如果两个对象属于相同(非泛型)类型,那么它们必须具有完全相同的成员(可能是可选的,但仍然是相同的一组键)。因此,以后不同对象具有不同键约束的唯一可能方法是它们具有技术上不同的类型。显然,泛型类型将是您的最佳选择,其中泛型参数会导致不同的键约束。

    假设我们这样定义一个泛型类型:

    type StyleSet<T extends string> = {
        [k in T]: React.CSSProperties;
    }
    

    但是,至少对于目前 TypeScript 的限制,它不能在不使用函数的情况下自动推断泛型参数。例如,你不能写:

    const a: StyleSet<> = ... // generic parameter must be explicitly given here
    

    因为目前还没有“部分类型推断”这样的东西;类型推断只有在我们完全忽略类型并且可以从分配的值中推断出来时才有效。如您所知,一种可能性是我们的函数解决方案:

    function set<T extends string>(s: StyleSet<T>) { return s; }
    
    const a = set({
        container: {
            width: "100%"
        }
    });
    

    然后在这种情况下,我们的a 被推断为StyleSet&lt;"containter"&gt;。是的,您必须将每个对象字面量包装在我们的函数 set() 中,但正如我们刚刚分析的那样,您唯一的选择是显式给出泛型参数:

    const a: StyleSet<"container"> = {
        container: {
            width: "100%"
        }
    };
    

    这显然更糟,尤其是当您在一个实例中有多个键时。使用函数解决方案,将一次性推断出所有键:

    const b = set({
        propA: { ... },
        propB: { ... }
    });
    // b will become StyleSet<"propA" | "propB">
    

    【讨论】:

    • 我知道这样,问题是键没有预定义。
    • 我已经更新了答案...您可能会感到失望,但实际上这是我们目前得到的最好的结果。
    • 非常感谢。够有趣的......这就像函数的魅力。如果你有一个函数而不是一个对象,你可以只使用函数的ReturnType 并且它可以工作。但是对于对象没有?奇怪。
    • 过去曾提出过部分推理,尽管进展不大。请参阅此 github 问题:github.com/microsoft/TypeScript/issues/26242
    猜你喜欢
    • 2012-12-26
    • 2023-01-12
    • 2021-01-11
    • 2019-08-29
    • 1970-01-01
    • 1970-01-01
    • 2014-06-10
    • 1970-01-01
    • 2010-12-14
    相关资源
    最近更新 更多