简体   繁体   中英

TypeScript parameters intersection reduced to never

If I have an object like this:

type GA = {
    name: 'GameA',
    duration: number
}

type GB = {
    name: 'GameB',
    duration: number
}

type Game = GA | GB;

type Initial<G extends Game = Game> = Pick<G, 'name'>

const Games = {
    GameA: {
        create: (initial: Initial<GA>): GA => {
            throw new Error('not implemented')
        }
    },
    GameB: {
        create: (initial: Initial<GB>): GB => {
            throw new Error('not implemented')
        }
    },
}

When trying to invoke the create of either by using an object with name , (property) name: "GameA" | "GameB" (property) name: "GameA" | "GameB" like this:

const game = Games[objectWithName.name].create(objectWithName)

reports an error:

Argument of type 'Game' is not assignable to parameter of type 'never'.
  The intersection 'Pick<GA, "name" | "duration"> & Pick<GB, "name" | "duration">' was reduced to 'never' because property 'name' has conflicting types in some constituents.
    Type 'GA' is not assignable to type 'never'.(2345)

How can I properly type my Games object or the objectWithName , so that the parameters aren't reduced to 'never'?

TypeScript Playground

This typescript playground minimal repro of the issue makes it clear that an Initial<GameA> can't be assigned to a GameA , given the Pick has eliminated some of the required fields.

This is the origin of your problem. This is also demonstrated by goodGame where using a valid GameA object as a 'prototype' DOESN'T face an issue in the create call.

type GA = {
    name: 'GameA',
    duration: number
}

type GB = {
    name: 'GameB',
    duration: number
}

type Game = GA | GB;

type Initial<G extends Game = Game> = Pick<G, 'name'>

const Games = {
    GameA: {
        create: (initial: Initial<GA>): GA => {
            throw new Error('not implemented')
        }
    },
    GameB: {
        create: (initial: Initial<GB>): GB => {
            throw new Error('not implemented')
        }
    },
}

const gameA : Game = {
    name:"GameA",
    duration:100
}

const initialGameA : Initial<Game> = {
    name:"GameA",
}

const goodGame = Games[gameA.name].create(gameA)

const badGame = Games[initialGameA.name].create(initialGameA)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM