如何在不忽略类型的情况下在打字稿中 'build an object'

How do I 'build an object' in typescript without ignoring types

嗨,我正在努力在打字稿中做一个常见的 javascript 模式,而不是使用 any 来忽略类型。我正在尝试编写一个根据某些条件构建对象的函数,并提供正确的 return 类型。我将其归结为以下示例:

是否可以在不使用 any 的情况下使用以下功能?

function hasAB<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    const obj: any = {}
    if (shouldHaveA) obj.a = ""
    if (shouldHaveB) obj.b = ""
    return obj
}
type HasAB<A extends boolean, B extends boolean> = 
    (A extends true ? {a: string} : {}) &
    (B extends true ? {b: string} : {}) 

const a = hasAB(true, false)
const b = hasAB(false, true)
const ab = hasAB(true, true)

return 类型对于 a、b 和 ab 是正确的,但是编译器不检查代码。您可以设置 obj.a 两次,它不会在意。

See code in the playground

这是我的尝试:

// attempt 1 and 2 fails because inference takes the union of conditionals e.g.
const five = true ? 5 : 4; // five: 5 | 4
function attempt1<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    return {
        ...(shouldHaveA ? { a: "" } : {}),
        ...(shouldHaveB ? { b: "" } : {})
    }
}
function attempt2<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    const obj1 = shouldHaveA ? {a: ""} : {}
    const obj2 = shouldHaveB ? {...obj1, b: ""} : obj1
    return obj2;
}

// attempt 3 can't get over the hurdle of needing to assign an intial type
function attempt3<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    let obj = {}
    if (shouldHaveA) {
        obj = { ...obj, a: "" }
    }
    if (shouldHaveB) {
        obj = { ...obj, b: "" }
    }
    return obj
}
// attempt 4 fails too
function attempt4<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    if (shouldHaveA) {
        const a = {a: ""}
    }
    if (shouldHaveB) {
        const b = {...(a || {}), b: ""}
    }
    const final = b || {}
    return final
}
// attempt 5 fails because ternary always assumes a union
function hasAB<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    const obj = {}
    const withA: A extends true ? {a: string} : typeof obj = shouldHaveA ? {a: ""} : obj
    const withB: withA & (B extends true ? {a: string} : {}) = shouldHaveB ? {...withA, b: ""} : withA
    return withB
}

TypeScript 尚不能执行此操作。最好的方法是利用现有的打字方式,例如在不保证最终结果的形状的情况下检查对象中的拼写错误。