Почему необязательный элемент объекта имеет тип «никогда»? - PullRequest
1 голос
/ 09 мая 2019

Я очень запутался со следующим небольшим фрагментом кода:

interface Test {
    one?: number,
    two?: {
        n: number
    }
}

const test: Test = {
    one: 1,
    two: { n: 2 }
}

function prop<O, K extends keyof O>(obj: O, key: K): O[K] {
    return obj[key]
}

let x = prop(test, 'one')   // Compiles ok, x is number | undefined
let y = prop(test.two, 'n') // Argument of type '"n"' is not assignable to parameter of type 'never'.

Здесь x напечатано как number | undefined, что ожидается.Но y набирается как never.Это почему?Я бы ожидал { n: number } | undefined вместо этого.

Чего мне не хватает, и как я могу заставить этот код скомпилироваться?

1 Ответ

0 голосов
/ 09 мая 2019

Причина, по которой это не удается, заключается в том, что в strictNullChecks необязательные поля имеют тип OriginalType | undefined. Доступны только общие члены объединения, и, поскольку undefined не имеет членов, это приводит к never.

Если вы наберете obj как obj: O | undefined | null, это вытянет null из O

function prop<O, K extends keyof O>(obj: O | undefined | null, key: K): O[K] | undefined | null {
    return obj && obj[key]
}

let x = prop(test, 'one')   // number | null | undefined
let y = prop(test.two, 'n') // number | null | undefined

Или версия, которая не вводит null и undefined без необходимости:

interface Test {
    one?: number,
    two?: {
        n: number
    }
    req: {
        n: number
    }
}

const test: Test = {
    one: 1,
    two: { n: 2 },
    req: { n: 2 }
}

function prop<O, K extends keyof Exclude<O, undefined | null>>(obj: O, key: K): Extract<O, undefined | null> | Exclude<O, undefined | null>[K]
function prop<K extends PropertyKey, V>(obj: Record<K, undefined | null | V>, key: K): V | undefined | null {
    return obj && obj[key]
}

let x = prop(test, 'one')   // number | undefined
let y = prop(test.two, 'n') // number | undefined
let z = prop(test.req, 'n') // number 
...