Как я могу написать функцию в TypeScript, которая применяет только строку - PullRequest
1 голос
/ 11 октября 2019

Мне нужна функция, которая позволяет передавать только ключи, если значение в объекте имеет string тип:

type GetNames<FromType, KeepType = any, Include = true> = {
    [K in keyof FromType]: 
        FromType[K] extends KeepType ? 
            Include extends true ? K : 
            never : Include extends true ? 
            never : K
}[keyof FromType];

const functionOnlyForStrings = <T>(obj: T, key: GetNames<T, string>) => {
    const t = obj[key]
    // do something with strings
    return t.toUpperCase()
}


const testObj: {a: string, b: number} = {a: 'test', b: 123}

const test = functionOnlyForStrings(testObj, 'a')
const wrongParam = functionOnlyForStrings(testObj, 'b')

В строках:

const test = functionOnlyForStrings(testObj, 'a') 
const wrongParam = functionOnlyForStrings(testObj, 'b') // here I get an error message

Все отлично работает. Если я передам b ключ, то TS покажет мне ошибку.

Но проблема в функции functionOnlyForStrings. Внутри этой функции TS не знает, что obj[key] всегда строка. И покажи мне ошибку:

Property 'toUpperCase' does not exist on type 'T[{ [K in keyof T]: T[K] extends string ? K : never; }[keyof T]]'.

Детская площадка

1 Ответ

2 голосов
/ 11 октября 2019

Это, по сути, ограничение дизайна TypeScript , по крайней мере, на данный момент.

Существует только так много, что мы можем ожидать, что компилятор поймет о манипулировании условными типами, которые зависят от универсального типапараметры. Возможно, компилятор может быть специально проверен, чтобы T[T[K] extends U ? K : never] можно было присвоить U. Но это будет стоить чего-то с точки зрения сложности проверки типов и времени компиляции, и любая выгода будет видна только некоторой части пользователей, которые специально делают подобные вещи. Это может стоить того, но я не задерживаю дыхание.

Между тем, у вас есть два основных способа с этим справиться. Один: разумное использование утверждения типа , чтобы сказать компилятору, что он не такой умный, как вы:

const functionOnlyForStrings = <T>(obj: T, key: GetNames<T, string>) => {
    const t = obj[key] as any as string;  // I'm smarter than you, compiler! ?
    return t.toUpperCase()
}

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

const functionOnlyForStrings = <
    T extends Record<K, string>, // constrain T to be a function of K
    K extends GetNames<T, string> // constrain K to be a function of T
>(obj: T, key: K) => {
    const t = obj[key]; // inferred as T[K]
    return t.toUpperCase() // no error
}

Это работает, потому что компилятор уже понимает, что {[P in K]: V}[K] будет назначено на V, и, таким образом, T[K]будет назначено на string.

Надеюсь, что поможет;удачи!

Ссылка на код

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