【问题标题】:TypeScript Says Intersection Property Does Not ExistTypeScript 说 Intersection 属性不存在
【发布时间】:2020-06-07 13:08:44
【问题描述】:

我有一个变量,它最初是一种类型,IPerson[],但在映射了几次之后,应该添加一个_id 属性,就像Array<IPerson & IWithId>。但是,在倒数第四行中,打印_id 属性会给我一个TS 错误,即使该属性确实存在并且日志记录工作如我所料,打印三个属性fnamelname_id

我想也许我需要以某种方式重新投射它,比如

mapped = collection.map(mapperB) as Array<IPerson & IWithId>

这没有用,幸好,对于一个 imo 应该已经根据 mapperB 函数的返回类型获取其类型的变量,这样做似乎非常冗长。

let _id = 0;
interface IPerson { 
    fname: string;
    lname: string;
}

interface IWithId { 
    _id: number;
}

function getNumber() { 
    return _id++
}

async function getData(json: string): Promise<IPerson[]> { 
    return JSON.parse(json)
}

function mapperA(entry: IPerson): IPerson { 
    return {
        ...entry,
        lname: entry.lname.toUpperCase()
    }
}
function mapperB(entry: IPerson): IPerson & IWithId { 
    const _id = getNumber();
    return {
        ...entry,
        _id
    } 
}
async function main() {
    const json = `[{"fname":"john","lname":"doe"},{"fname":"jane","lname":"doe"}]`    
    const collection = await getData(json)
    let mapped = collection.map(mapperA)
    mapped = collection.map(mapperB)
    console.log(mapped[0]._id); // Property '_id' does not exist on type 'IPerson'.
    return mapped;
}

main().then(console.log)

如果我使用另一个变量来保存第二个映射函数的值,即const mapped2 = collection.map(mapperB),我可以让它工作,但我很好奇为什么我不能使用我的原始变量?

为什么打字稿不从mapperB的明确声明的返回值推断mapped的值?我可以让它为我做这件事吗?

TypeScript Playground

【问题讨论】:

  • 以下关于无法更改变量类型的答案是正确的,但您仍然可以通过链接调用来使用单个变量,如果这就是您所追求的:let mapped = collection.map(mapperA).map(mapperB)。结果是(IPerson &amp; IWithId)[]

标签: javascript typescript


【解决方案1】:

TypeScript 从它的第一次赋值(初始化)推断mapped 的类型,所以它是IPerson[]

In TypeScript, there are several places where type inference is used to provide
type information when there is no explicit type annotation. For example, in this
code

> let x = 3;

The type of the x variable is inferred to be number. This kind of inference takes place
when initializing variables and members, setting parameter default values, and 
determining function return types.

取自"Type inference" chapter from the TypeScript handbook(我链接了它即将发布的 2.0 beta 版本),我推荐阅读这篇文章。

第二个赋值然后不扩展定义,但也没有错误,因为对象可以有额外的属性。当您访问 _id 时,您会收到错误消息,因为 TypeScript 无法从最初推断的类型确定数组条目还包含 _id 属性。

注意:使用mapped = collection.map(mapperB) as Array&lt;IPerson &amp; IWithId&gt; 进行强制转换不会给 TypeScript 额外的信息,所以结果是一样的。


为了简化关于类型的推理,我个人建议将转换后的值分配给新变量(正如您在 const mapped2 = collection.map(mapperB) 中提出的那样。并选择富有表现力的变量名称(权衡变得冗长,但这不应该)如果你的函数复杂度足够小,这种情况就不会经常发生):

const filteredList = list.filter(...);
const filteredListWithIds = filteredList.map(...)

没有直接关系,但有错误:Array.prototype.map()returns a new array。来自let mapped = collection.map(mapperA)mapped 的值立即丢失,因为它s being overwritten at the next line duringmapped = collection.map(mapperB)`。根据您的真实代码创建 Playground 示例时可能会出错?

【讨论】:

    【解决方案2】:

    是的,你不能在 typescript 中更改变量的类型,一旦它被赋值。

    如上例所述,您可以使用不同的变量。 但根据您的担忧,您只想使用一个变量,您可以通过将它们一个接一个地链接来调用两个映射器。

    Typescript 以非常好的方式支持函数调用的链接。 因此,您可以将最后两行代码替换为以下单行代码:

    let mapped = collection.map(mapperA).map(mapperB)
    

    我希望这对您有所帮助。你可以解决你的错误。

    【讨论】:

      【解决方案3】:

      问题出在以下几行:

      let mapped = collection.map(mapperA) // here you declare mapped with the type IPerson[]
      mapped = collection.map(mapperB) // here mapped already has a type and can't be changed
      console.log(mapped[0]._id); // here you try to access a property IPerson doesn't have
      

      您可以尝试按照其他答案链接映射器或仅将两个映射器强制为一个来解决此问题:

      function mapper(entry: IPerson): IPerson & IWithId {
          const _id = getNumber();
      
          return {
              ...entry,
              _id,
              lname: entry.lname.toUpperCase()
          }
      }
      
      // later in your main function
      let mapped = collection.map(mapper); // here mapped is declared as (IPerson & IWithId)[]
      console.log(mapped[0]._id); // now you can access any IWithId property
      

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2016-07-05
        • 1970-01-01
        • 2014-07-18
        • 1970-01-01
        • 2017-11-28
        • 2017-06-03
        • 2023-04-02
        • 2021-08-31
        • 2018-08-17
        相关资源
        最近更新 更多