首页 > 解决方案 > TypeScript 说 Intersection 属性不存在

问题描述

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

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

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

mapperB那没有用,谢天谢地,因为对于 imo 应该已经根据函数的返回类型获取其类型的变量,必须这样做似乎非常冗长。

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)但我很好奇为什么我不能使用我的原始变量?

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

打字稿游乐场

标签: javascripttypescript

解决方案


TypeScriptmapped从它的第一个赋值(初始化)推断出类型,所以它是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.

摘自TypeScript 手册中的“类型推断”一章(我链接了它即将发布的 2.0 beta 版本),我建议阅读这个。

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

注意:casting withmapped = collection.map(mapperB) as Array<IPerson & IWithId>不会给 TypeScript 额外的信息,所以结果是一样的。


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

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

没有直接关系,而是一个错误:Array.prototype.map() 返回一个数组mappedfrom的值在映射 = collection.map(mapperB)`let mapped = collection.map(mapperA)时立即丢失。s being overwritten at the next line during根据您的真实代码创建游乐场示例时可能会出错?


推荐阅读