TypeScript说交叉点属性不存在。

问题描述 投票:0回答:1

我有一个变量,开始是一种类型。IPerson[]但经过几次映射,应该会有一个 _id 属性,所以像 Array<IPerson & IWithId>. 然而,在第四行到最后一行,暴虐地打印出了 "我 "的名字。_id 属性给了我一个TS错误,尽管该属性确实存在,而且日志记录也像我所期望的那样,打印了三个属性。fname, lname_id.

我想也许我需要以某种方式重新铸造它,就像。

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

这没有用,幸好,因为对于一个变量来说,必须这样做似乎是超级啰嗦的,因为这个变量应该已经根据返回类型得到了它的类型。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) 但我很好奇为什么我不能使用我原来的变量?

为什么typescript不能推断出这个变量的值?mapped 的明确声明的返回值。mapperB? 我可以让它为我做这个吗?

TypeScript游乐场

javascript typescript
1个回答
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.

取自: TypeScript手册中的 "类型推理 "章节。 我链接了它即将推出的2.0测试版),我推荐大家看这个。

第二项任务则是 不延伸 的定义,但也不会出错,因为对象可以有额外的属性。当你在访问 _id,你会得到一个错误,因为TypeScript不能从最初推断的类型中确定数组条目也包含了 _id 属性。

注意:用 mapped = collection.map(mapperB) as Array<IPerson & IWithId> 没有给TypeScript提供额外的信息,所以结果是一样的。


为了便于对类型进行推理,我 亲自 建议将转换后的值分配给新的变量(就像您对 const mapped2 = collection.map(mapperB). 并选择富有表现力的变量名(有变得冗长的权衡,但如果你保持你的函数复杂度足够小,这种情况应该不会经常发生)。

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

不直接相关,但也是一个错误。Array.prototype.map() 返回一个 新的 阵列. 价值 mappedlet mapped = collection.map(mapperA) 得而复失s being overwritten at the next line duringmapped = collection.map(mapperB)`。也许是根据你的真实代码创建游乐场示例时的错误?


0
投票

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

正如上面的例子中提到的,你可以使用不同的变量.但根据你的关注,你想简单地使用一个变量,你可以通过链式调用这两个mappers一个接一个。

Typescript支持以非常好的方式链式调用函数,所以你可以用下面的单行代码替换你的最后两行代码。

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

希望对你有帮助 并且你可以解决你的错误。


0
投票

这里的问题出在下面几行。

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

希望能帮到你

© www.soinside.com 2019 - 2024. All rights reserved.