Typescript: как ограничить выбор свойства свойствами универсального параметра и дополнительным типом?

#typescript

#typescript

Вопрос:

Пример ограничения выбора свойства только свойствами универсального параметра T :

 type Person = {
  name: string;
  age: number;
  locations: number[];
}

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const p1: Person = { name : "Foo", age: 55, locations: [1,2,3,4] };
const name = getProperty(p1, "locations"); // Output: [1,2,3,4]
 

Чего я хочу добиться сейчас, так это ограничить K выбор ключа объекта не только keyof T определенным типом, но и K определенным типом, например: Массив. Пример использования:

 const name = getProperty<Array<number>>(p1, "name");      // Should fail as name is not Array<number>
const name = getProperty<Array<number>>(p1, "locations"); // Should succeed
 

Ответ №1:

Максимум, чего я могу достичь: игровая площадка

Используя эту статью для решения :

 type SubType<Base, Condition> = Pick<Base, {
    [Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base]>;
 

 type Person = {
  name: string;
  age: number;
  locations: number[];
}

function getProperty<T, U extends T[keyof T], K extends SubType<T, U> = SubType<T, U>>(obj: T, key: keyof K): U {
  return obj[key as unknown as keyof T] as U;
}

type SubType<Base, Condition> = Pick<Base, {
    [Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base]>;

// Examples --> Hover on D to see SubType result
// https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
type D = SubType<Person, number>;
// --------------------

const p1: Person = { name : 'Foo', age: 55, locations: [1,2,3,4] };

// Should fail as foo is not in Person
const var1 = getProperty(p1, 'foo');     

// Should fail as name is not of type Array<number> in Person
const var2 = getProperty<Person, Array<number>>(p1, 'name');    

// Should succeed as name is in Person
const var3 = getProperty(p1, 'name');    

// Should succeed as locations is in Person and is a number[]
const var4 = getProperty<Person, number[]>(p1, 'locations'); 

// Should succeed as age is in Person and is a number
const var5 = getProperty<Person, number>(p1, 'age'); 

// Should fail as age is in Person and is not a string
const var6 = getProperty<Person, string>(p1, 'age'); 

console.log(var1);
console.log(var2);
console.log(var3);
console.log(var4);
console.log(var5);
console.log(var6);
 

Комментарии:

1. Ничего себе выдающийся пример. Я никогда не понимаю, где границы между ограничениями встроенного типа в области универсального параметра <> и отдельным типом. Я думаю, что после его разделения он становится намного чище для чтения. Большое спасибо!