gpt4 book ai didi

typescript - 字符串作为变量和映射键类型的行为不同

转载 作者:行者123 更新时间:2023-12-02 01:55:57 25 4
gpt4 key购买 nike

考虑一下:

type N = never;
type A = 'A';
type B = 'A' | 'B';
type S = string;

type RN = Record<N, string>;
type RA = Record<A, string>;
type RB = Record<B, string>;
type RS = Record<S, string>;

declare let n : N;
declare let a : A;
declare let b : B;
declare let s : S;

s = b;
b = a;
a = n;

declare let rn : RN;
declare let ra : RA;
declare let rb : RB;
declare let rs : RS;

rn = rs;
rs = rn;

rs = ra;
ra = rb;

<是子类型运算符。显然,N < A < B < S因为n可分配给 a可分配给 b可分配给 s .

所以,我期望RS < RB < RA < RN .

但是,从示例中您可以看到RB < RA < RS因为rb可分配给 ra可分配给 rs 。此外,RSRN似乎是等效类型。

我假设 string可以看作是所有 string 的联合类型文字类型。所以实际上RS应等于 never因为不可能有一个对象具有所有可能存在的字符串文字的属性(取无限空间)。将此称为完整对象。

但是看起来像RS实际上相当于空( RN )且不完整的对象。

为什么是string表现得像 neverRecord

最佳答案

Mapped typesthe Record<K, V> utility type map stringnumber literal各个属性的键,因此 Record<"A" | "B", string>相当于 {a: string; b: string} .

但是宽的、非文字类型的键,例如 string本身,或 number ,或模式模板文字类型,如 `foo${string}` (如 microsoft/TypeScript#40598 中实现)映射到 index signatures 。来自索引签名的文档:

Sometimes you don’t know all the names of a type’s properties ahead of time, but you do know the shape of the values. In those cases you can use an index signature to describe the types of possible values.

因此索引签名并不真正代表具有相关类型的所有可能的键的“完整对象”,例如无限 intersection所有单键对象的数量 {a: string} & {b: string} & {c: string} & ... & {foo: string} & ... {blahblah: string} & ... .

(旁白:你说一个完整的对象相当于 never 因为这是不可能的。但这并不准确。一个 Proxy 对象可以很容易地被制作成符合这种类型。即使它在 JavaScript 中是不可能的,你不希望类型系统将其视为 never ,而不具有某种关于无穷大的显式公理,这并不明显,然后你就可以必须弄清楚如何在不禁止递归数据类型的情况下做到这一点。)

无论如何,索引签名更像是属性上的约束{[k: IndexType]: ValType} 形式的索引签名表示“如果该对象具有 IndexType 类型的属性键,那么这样的属性将具有 ValType 类型的值”。从某种意义上来说,它更像是所有单键对象与 optional properties 的无限交集。 ,例如{a?: string} & {b?: string} & {c?: string} & ... & {foo?: string} & ... {blahblah?: string} & ...


当然,情况比这更复杂,因为编译器传统上并不以相同的方式对待索引签名和可选属性。

在 TypeScript 4.1 之前,索引签名始终允许您读取属性并获取值,即使我刚刚解释完它们如何更像可选属性。对此有很多提示,因此 TypeScript 4.1 引入了 the --noUncheckedIndexedAccess compiler flag ,其中添加了 undefined读取时到索引签名属性值的域,但写入时不到索引签名属性值的域。默认情况下,即使使用--strict,它也不会启用。 ,因为虽然它的类型更安全,但在人们通过数组或对象进行索引的任何情况下,它都会很烦人......像 for (let i=0; i<arr.length; i++) {arr[i]} 这样的代码或Object.keys(obj).forEach(k => obj[k])从技术上讲应该显示 arr[i]obj[k]可能 undefined ,至少没有办法跟踪 i身份k而不仅仅是类型

在 TypeScript 4.4 之前,可选属性被视为具有 undefined在阅读和写作时都将其视为其领域的一部分。人们对此也提示很多,因此 TypeScript 4.4 引入了 the --exactOptionalPropertyTypes compiler flag其中保留了undefined读取,但拒绝写入 undefined 的属性。这也不包含在 --strict 中,因为类似 foo.bar = foo.bar如果 bar 现在被视为错误是可选的。

如果您启用这两个编译器标志,则索引签名和可选属性具有类似的行为,尽管我确信存在更多边缘情况。


无论如何... Record<string, string>相当于 {[k: string]: string} )同时Record<never, string>相当于 empty object type {} 。这些不是相同的类型,但由于与 microsoft/TypeScript#7029 中实现的隐式索引签名有关的规则,它们是相互兼容的。 .

那里还有很多东西需要解开,我们可以花很长一段时间来了解weak type detection。 , excess property checking ,以及索引签名和 interface 之间的交互类型(参见 microsoft/TypeScript#15300 )。不过,我现在要停下来,因为这个答案已经足够长了。

关于typescript - 字符串作为变量和映射键类型的行为不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69596074/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com