I have the following type definitions.
type Key<Row> = {
[P in keyof Row]: Row[P] extends string ? P : never
}[keyof Row];
type ID<Row> = Row[Key<Row>];
type Selected<Row> = {
selected: boolean;
} & Row;
Key<Row> returns all the property keys of Row whose values are strings. See this answer for how it is constructed.
ID<Row> is a union of all the string values of Row. It is always a string but narrows nicely if the string value is a string constant, e.g. in
interface X {
prop: "abc";
}
ID<X> is always "abc".
Selected<Row> is an intersection type of whatever Row is plus the property selected: boolean.
With all the above defined, I don't understand why the below function definition throws an error:
const getId = <Row>(row: Selected<Row>, key: Key<Row>): ID<Selected<Row>> =>
row[key];
The type error TS gives me is pasted below, but I can't work out why TS doesn't like my code.
From what I can see row[key] should always work because even if row is Selected<Row>, Key<Row> only contains a narrower set of keys, so what's the problem with keying into an object that has more properties than the key we're actually using?
Here is a link to the TS playground with this code showing the error
Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
Type 'Selected<Row>[keyof Row]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
Type 'Row[string] | Row[number] | Row[symbol]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
Type 'Row[string]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
Type 'Row[string]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
Type 'Row' is not assignable to type '{ selected: boolean; }'.
Type 'Row[string]' is not assignable to type 'boolean'.
Type 'Selected<Row>[keyof Row]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
Type 'keyof Row' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'keyof Row' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
Type 'Selected<Row>[keyof Row]' is not assignable to type 'boolean'.
Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type 'Selected<Row>["selected"] extends string ? "selected" : never'.
Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type 'boolean'.
Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'keyof Row' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'string | number | symbol' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'string' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
Type 'string' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
Type 'keyof Row' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
Type 'string | number | symbol' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
Type 'string' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type 'Selected<Row>["selected"] extends string ? "selected" : never'.
Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type 'boolean'.
Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type 'boolean'.
Type 'Selected<Row>[keyof Row]' is not assignable to type 'boolean'.
Type 'Row[string] | Row[number] | Row[symbol]' is not assignable to type 'boolean'.
Type 'Row[string]' is not assignable to type 'boolean'.