Это может быть строго напечатано? - PullRequest
0 голосов
/ 21 января 2019

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

ВотСценарий, который я хотел бы использовать:

type Shape = 
    | { kind: 'Circle', radius: number }
    | { kind: 'Rectangle', height: number, width: number }

function printShape(s: Shape) {
    return document.write(
        match(s, {
        'Circle': c => `Circle(${c.radius})`,
        'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
    }));
}

Моя текущая попытка определения функции соответствия выглядит следующим образом:

function match<T extends { kind: V }, V extends string, R>(
    x: T, branches: { [P in T['kind']]: (arg: T & { 'kind': P }) => R }) {

    return branches[x.kind](x);
}

Это близко, но, к сожалению, не 'т совсем работа;в то время как я успешно получил компилятор, чтобы жаловаться на полноту для заданных совпадений, аргументы для функций ветвления не очень хорошо типизированы: аргументы c и r имеют тип any.

IЯ согласен с наличием kind в качестве жестко заданного дискриминатора, но я обычно не понимаю в Typescript, как отфильтровывать возможности из универсального объединения типов.Например, я сосредоточил свое упражнение на попытке написать следующее:

type Where<T, K extends keyof T, V extends T[K]> = ???

У меня правильные ограничения типов в том, что я получаю правильную проверку моих типов и литералов от компилятора при написании:

type Circle = Where<Shape, 'kind', 'Circle'>

но я не понимаю, что я могу написать справа от этого выражения типа, чтобы вернуть:

{ kind: 'Circle', radius: number }

1 Ответ

0 голосов
/ 21 января 2019

Чтобы извлечь конкретный тип из объединения, вы можете использовать условный тип Extract. Это извлечет всех членов объединения, которые являются подклассом второго параметра. Ваш тип where будет выглядеть так:

type Where<T, K extends keyof T, V extends T[K]> = Extract<T, Record<K, V>>
type C = Where<Shape, 'kind', 'Circle'> //{ kind: 'Circle', radius: number }

Вы можете получить полные типы для функции, если используете функцию, которая возвращает функцию. Первый вызов устанавливает T, а второй вызов может использовать информацию о типе для полного ввода параметров:

type Shape =
  | { kind: 'Circle', radius: number }
  | { kind: 'Rectangle', height: number, width: number }

function printShape(s: Shape) {
  var r = match(s)({
    'Circle': c => `Circle(${c.radius})`,
    'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
  }) // r is string
  return document.write(r);
}


function match<T extends { kind: V }, V extends string>(x: T) {
  return function <R>(branches: { [P in T['kind']]: (arg: Extract<T, { 'kind': P }>) => R }) {

    return branches[x.kind](x as any);
  }
}
...