Можно ли ограничить универсальный тип подмножеством keyof в TypeScript?

В текущей версии (2.1) TypeScript я могу ограничить аргумент метода универсального класса свойством универсального типа.

class Foo<TEntity extends {[key:string]:any}> {
    public bar<K extends keyof TEntity>(key:K, value:TEntity[K]) { }
}

Возможно ли в текущей системе типов ограничить ключевую часть еще больше, чтобы она была подмножеством, где значение ключа имеет определенный тип?

То, что я ищу, это что-то вроде этого псевдокода.

class Foo<TEntity extends {[key:string]:any}> {
    public updateText<K extends keyof TEntity where TEntity[K] extends string>(key:K, value:any) {
        this.model[key] = this.convertToText(value);
    }
}

ИЗМЕНИТЬ

Для пояснения я добавил более полный пример того, чего я пытаюсь достичь.

type object = { [key: string]: any };

class Form<T extends object> {
    private values: Partial<T> = {} as T;

    protected convert<K extends keyof T>(key: K, input: any, converter: (value: any) => T[K])
    {
        this.values[key] = converter(input);
    }

    protected convertText<K extends keyof T>(key: K, input: any)
    {
        this.values[key] = this.convert(key, input, this.stringConverter);
    }

    private stringConverter(value: any): string
    {
        return String(value);
    }
}

Демо на typescriptlang.org

convertText выдаст ошибку о том, что Type 'string' is not assignable to type 'T[K]'.

Дано

interface Foo {
    s: string
    n: number
}

Компилятор может сказать, что это будет работать

this.convert('s', 123, v => String(v));

и этого не будет

this.convert('n', 123, v => String(v));

Я надеюсь, что смогу ограничить метод convertText ключами, где значение имеет тип string, чтобы обеспечить безопасность типов для ключевого параметра.


person opex    schedule 17.01.2017    source источник
comment
Не совсем понятно, что вы спрашиваете. Вы имеете в виду значение на карте TEntity? Или ключ? Можете ли вы отредактировать свой вопрос и привести пример того, как вы будете его использовать?   -  person Nitzan Tomer    schedule 17.01.2017
comment
Псевдокод — это пример того, как я буду его использовать. Это выдаст ошибку, что this.model[key] не может быть назначена строка, поскольку TEntity[K] не объявлена ​​как строка.   -  person opex    schedule 17.01.2017
comment
Нет, нет никакого способа сделать это. Компилятор не может узнать, является ли TEntity[K] строкой или нет, вы определили значения как any, так как же компилятор может знать?   -  person Nitzan Tomer    schedule 17.01.2017


Ответы (1)


Это возможно (здесь используется неклассовый пример). Следующее гарантирует, что T[P] является строкой.

function convertText<T extends {[key in P]: string }, P extends keyof T>(data: T, field: P & keyof T) {
    // ...
}

Идея состоит в том, чтобы сузить тип T только до полей, выведенных в P, и установить точный тип, который вы хотите, в данном случае string.

Контрольная работа:

let obj = { foo: 'lorem', bar: 2 };
convertText(obj, 'foo');
convertText(obj, 'bar'); // fails with: Type 'number' is not assignable to type 'string'.
person altschuler    schedule 05.03.2017
comment
Отлично работал и с классами, но не смог заставить его работать с интерфейсами. слава - person Thodoris Greasidis; 17.11.2017
comment
Это похоже на очень круговой набор ограничений. Я удивлен, что это может быть решено. Как мы могли бы сделать подобное ограничение, используя keyof this? т.е. где функция (поле: ключ этого) и это [поле] имеют тип ClassA? - person jcairney; 16.10.2019
comment
Я понятия не имею, почему, но это работает на стороне вызывающего. Но теперь, внутри функции convertText, как заставить компилятор знать, что значение obj['foo']=some совершенно верно? Прямо сейчас у меня есть ошибка компиляции, говорящая, что TS2322: Type 'string' is not assignable to type 'T[P & keyof T]'. - person Antoniossss; 21.05.2021
comment
@altschuler, возможно, вы могли бы взглянуть на этот stackoverflow.com/questions/67642380/ - person Antoniossss; 21.05.2021