代码之家  ›  专栏  ›  技术社区  ›  Zoddo

属性在明确定义时可能为“未定义”

  •  2
  • Zoddo  · 技术社区  · 5 月前

    考虑到这段非常简单的代码:

    type ExempleType = {
      someProperty?: string[];
    };
    
    const someVar: ExempleType = { someProperty: [] };
    
    someVar.someProperty.push('test'); // => 'someVar.someProperty' is possibly 'undefined'.ts(18048)
    

    我不明白为什么typescript在最后一行返回错误。 someProperty 不能 未定义,因为它是明确定义的。

    如果我稍微更新代码并定义 一些财产 在变量实例化之后,它就开始工作了:

    type ExempleType = {
      someProperty?: string[];
    };
    
    const someVar: ExempleType = {};
    someVar.someProperty = [];
    
    someVar.someProperty.push('test'); // => No error!
    

    在我看来,这似乎是Typescript中的一个错误……但另一方面,我希望这样的错误能很快被发现(并修复),所以它可能不是一个错误。

    那么,我是否遗漏了一些可以解释这个错误的东西?

    2 回复  |  直到 5 月前
        1
  •  1
  •   jcalz    5 月前

    如果你 annotate a variable a(非- union )类型类似 ExempleType ,那么这就是TypeScript所知道的关于该变量类型的全部信息。关于您为其分配的值类型的任何更具体的信息都不会被跟踪。有这样一件事 assignment narrowing ,但这只适用于 联盟 类型。所以如果你写 let x: string[] | undefined = [] 那么 x 缩小到 string[] 在那次任务之后。但相似的外观 let y: { x: string[] | undefined } = { x: [] } 不以相同的方式工作,因为您正在分配一个非联合对象类型,其 财产 恰好是一个联盟。如果作业缩窄也适用于非联合类型,那就太好了,但事实并非如此(参见 this comment on microsoft/TypeScript#8513 了解更多信息)。

    这就解释了 const someVar: ExempleType = { someProperty: [] }; const someVar: ExempleType = {}; someVar.someProperty = []; 在后一种情况下,您已经分配了属性,这实际上是一个联合类型( optional properties 行为与工会相似 undefined )因此,它被缩小到 string[] 后续的直接财产访问,如 someVar.someProperty 然后会看到 string[] 但是 someVar 其本身不会变窄:

    const someVar: ExempleType = {};
    someVar.someProperty = [];
    someVar.someProperty.push('test'); // no error
    function foo(r: Required<ExempleType>) { }
    foo(someVar); // error!  Types of property 'someProperty' are incompatible.
    

    一般来说,如果你想让TypeScript跟踪初始化器中的特定信息(而且你不打算做很多突变),那么你可能想完全避免注释,并使用 the satisfies operator 检查初始化器是否与该类型兼容。

    空数组有点乱,因为 [] 最终将被推断为 never[] ,即使有 满足 .有一个悬而未决的问题 microsoft/TypeScript#51853 .现在使用的“安全”方式 满足 冗长:

    const someVar = {
        someProperty: [] satisfies string[] as string[]
    } satisfies ExempleType;
    
    /* const someVar: { someProperty: string[]; } */
    

    这个 expr satisfies T as T 施工有效地首先检查 expr 可分配给 T 然后 扩大到 T ,所以 [] satisfies string[] as string[] 最终以 [] 作为类型 string[] 在检查它是否工作后。写起来更容易 [] as string[] 但这有点不安全。然后 满足 最后确保 { someProperty: string[] } 与兼容 示例类型 ,它是…但是 someVar 现在比 示例类型 ;它是 someProperty 众所周知 string[] ,其余代码按预期工作:

    someVar.someProperty.push('test'); // okay
    function foo(r: Required<ExempleType>) { }
    foo(someVar); // okay
    

    Playground link to code

        2
  •  1
  •   Jim    5 月前

    Typescript告诉您属性的原因可能是 undefined 因为这就是你告诉它的。当你写作时 const x: T = y ,字体处理 x 作为 T ,无论实际值如何 y (虽然 y 必须可转让给 T ).

    如果你真的希望Typescript使用以下类型 y 对于 x ,您可以使用 const x = y satisfies T ,告诉Typescript 检查 那个 y 可分配给 T 没有 铸造 y T .

    推荐文章