Ограничение универсального сопоставленного типа с необязательным членом не ограничивает, когда один из членов существует

const abc = <T extends object, P extends { [key in keyof T]?: number }>(
  a: T,
  b: P
) => {
  console.log(a, b);
};

const A = { x: "1", y: "2", z: "3" };
const b = { x: 1, y: 2, z: 3 };
const b1 = { x: 1, y: 2 };
const b3 = { m: 5 };
const b4 = { m: 5, x: 1 };

abc(A, b);
abc(A, b1);
abc(A, b3); // Type '{ m: number; }' has no properties in common with type '{ x?: number | undefined; y?: number | undefined; z?: number | undefined; }'
abc(A, b4); // expect type error just like b3 but it is not

так как m не существует на A, b4 должно выдавать ошибку вроде b3, верно? но почему это не ошибка и как это исправить?

вот песочница codesandbox


person tylim    schedule 04.05.2020    source источник
comment
Typescript по умолчанию допускает дополнительные свойства, поэтому { m: 5 } считается допустимым аргументом. Этот пост может вам помочь: stackoverflow.com/questions/49580725/   -  person Simon Bruneaud    schedule 04.05.2020
comment
@Simon, спасибо за ссылку, довольно информативно, но решение в ней не работает, потому что ему нужен не только точный член (что я хочу), он также должен быть точно такого же типа (чего я не хочу)   -  person tylim    schedule 05.05.2020


Ответы (2)


Введите '{ м: число; }' не имеет общих свойств с типом '{ x?: число | неопределенный; у?: номер | неопределенный; я?: число | неопределенный; }.

думаю, ясно, что b4 не является ошибкой, потому что имеет одно из значений x?, y? г? включены. это потому, что вы простираетесь от T, а T - от первого параметра, который вы передали, это A.

person Sam Yao    schedule 05.05.2020

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

const abc = <T extends { [index: string]: string }>(
  a: T,
  b: { [key in keyof T]?: number }
) => {
  console.log(a, b);
};

const A = { x: "1", y: "2", z: "3" };
const b4 = { m: 5, x: 1 };

abc(A, b4); // no error, not "expected"
abc(A, { m: 5, x: 1 }); // exact same value as b4 but throw error as "expected"

решение: вам нужно передать новое значение (значение, которое не является переменной)

для объяснения и другого решения (вспомогательного типа) прочитайте этот ответ и его комментарии

update, я нашел идеальное решение:

const abc = <
  T extends { [index: string]: string },
  U extends { [key in keyof T]?: number }
>(
  a: T,
  b: keyof U extends keyof T ? U : never
) => {
  console.log(a, b);
};

const A = { x: "1", y: "2", z: "3" };
const b4 = { x: 1, y: 2, z: 3 };
const b5 = { m: 2, x: 5 };
const b6 = { x: 1, y: 2, z: 3, m: 2 };
const b7 = { x: 1 };

abc(A, b4); // no error, excat member
abc(A, b5); // error, excess member
abc(A, b6); // error, excess member
abc(A, b7); // no error, one of the member exist
abc(A, {}); // no error, since it is optional

// all work as expected

эта работа вне зависимости от ценности свежа она или нет

person tylim    schedule 05.05.2020