Сделать вывод о сокращенном типе объединения, проверив наличие свойства?

У меня действительно большой тип объединения с членами, которые имеют множество типов с некоторыми перекрытиями. На практике это все различные типы свойств записи в действительно большой схеме базы данных.

    type A = { x: number; y: number }
    type B = { y: number; z: number }
    // many many more types

    type All = 
        | A 
        | B
        // many many more types

У меня есть очень общая функция, которая принимает любой тип и что-то делает в зависимости от наличия определенных свойств. Проблема в том, что я не могу проверить наличие свойства, потому что Typescript выдаст ошибку и сообщит, что некоторые типы в этом объединении не имеют этого свойства. Но в идеале я мог бы проверить наличие, и Typescript определит тип на основе существования этого свойства ...

    function f(value: All) {
        if (value.x) {
            // value must be A or anything that has an x property
        } else if (value.y) {
            // value must be B or anything that has an x property       
        }
    }

Есть идеи, как это сделать?


person Chet    schedule 03.08.2017    source источник
comment
Вы не описываете помеченный союз; это просто союз.   -  person jcalz    schedule 03.08.2017


Ответы (2)


Да, ты можешь это сделать:

type A = { x: number; y: number }
type B = { y: number; z: number }
type C = { z: number; x: number }
type D = { y: number; w: number }
type E = { w: number; z: number }
type All = A | B | C | D | E ;

function hasKey<K extends string>(key: K, val: any): val is {[P in K]: any} {
  return key in val;
}

function f(v: All) {
  if (hasKey('x', v)) {
    v // A | C
  } else if (hasKey('y',v)) {
    v // B | D
  } else {
    v // E
  }
}

Функция hasKey - это определяемый пользователем тип защиты, который сузит для вас количество объединений.

person jcalz    schedule 03.08.2017
comment
Вау, я бы никогда этого не узнал. Есть ли где-нибудь библиотека с такими функциями? Должно быть - person Chet; 04.08.2017

Основываясь на ответе jcalz, я предпочитаю подход, который не требует специальной библиотечной функции:

type A = { x: number; y: number }
type B = { y: number; z: number }
type C = { z: number; x: number }
type D = { y: number; w: number }
type E = { w: number; z: number }
type All = A | B | C | D | E ;

function f(v: All) {
  if ('x' in v)) {
    v // A | C
  } else if ('y' in v)) {
    v // B | D
  } else {
    v // E
  }
}

Я думаю, что этот подход даже лучше понять, прочитав.

person htho    schedule 07.11.2019