Правильный вывод типа с помощью keyof в универсальных функциях - PullRequest
1 голос
/ 17 марта 2019

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

Я попытался реализовать ее следующим образом.

type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp ? P : never }[keyof T];

function getLen<T>(obj: T, p: KeysOfType<T, string>): number {
    return obj[p].length
}

Но компилятор выдает ошибку с сообщением "Свойство" length "существует в типе 'T [{[P в keyof T]: T [P] расширяет TProp? P: никогда}} [keyof T]] '".

Почему компилятор не считает, что я ограничил набор возможных ключей только ключами, соответствующими значению типа string?Как это исправить?

1 Ответ

2 голосов
/ 17 марта 2019

Компилятор просто не достаточно умен.Условные типы, которые зависят от универсальных параметров (например, KeysOfType<T, string>), обычно обрабатываются компилятором как непрозрачные, и хотя , вы понимаете, что KeysOfType<T, V> был специально сконструирован так, чтобы гарантировать, что T[KeysOfType<T, V>] extends Vправда, компилятор даже не пытается.

Самое общее решение, доступное нам в подобных случаях, - это использование утверждения типа .Например, вы можете сказать компилятору не беспокоиться и рассматривать obj[p] как string:

function getLen<T>(obj: T, p: KeysOfType<T, string>): number {
    return (obj[p] as unknown as string).length;
    // the type T[{ [P in keyof T]: T[P] extends string ? P : never; }[keyof T]]
    // is so opaque to the compiler that we must widen to unknown 
    // before narrowing to string
}

Обратите внимание, что вы освобождаете компилятор от обязанности проверки безопасности типов.Вы могли бы так же легко сказать obj[p] as unknown as boolean, и компилятор поверил бы вам.Так что используйте эту мощность с осторожностью.


Другой способ сделать подобное - использовать одну перегрузку функции , чтобы различать общий условный тип, который видят вызывающая сторона инадеюсь, более гибкий тип, как видно из реализации:

// call signature, unchanged
function getLen<T>(obj: T, p: KeysOfType<T, string>): number;

// implementation signature... let's make p the generic type K
// and say that obj has keys K and values of type string
function getLen<K extends keyof any>(obj: Record<K, string>, p: K): number {
  return obj[p].length;
}

Причина, по которой он похож на утверждение типа, заключается в том, что компилятор позволяет вам сделать сигнатуру реализации более свободной, чем сигнатуры вызова ... если вы 'не будьте осторожны, вы можете лгать компилятору, и вы не увидите проблемы до времени выполнения.


Хорошо, надеюсь, это поможет.Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...