The keyof
operator produces a union type of all known, public property names of a given type. You can use it together with lookup types (aka indexed access types) to statically model dynamic property access in the type system.
is there any convenient way to use Keyof on sub-properties? Like
interface Attributed {
attributes: {
[k: string]: v: any;
}
}
interface AttributedTodo extends Attributed {
id: number;
attributes: {
title: string;
}
}
function getAttribute<T extends Attributed, K keyof T.attributes>(item: T, key: K) {
return item.attributes[key];
}
getAttribute(todo, "title");
(which doesn't compile)
Hi Eric,
try this version:
interface Attributed {
attributes: {
[k: string]: any;
}
}
interface AttributedTodo extends Attributed {
id: number;
attributes: {
title: string;
completed: boolean;
}
}
function getAttribute<T extends Attributed, K extends keyof T["attributes"]>(item: T, key: K): T["attributes"][K] {
return item.attributes[key];
}
const todo: AttributedTodo = {
id: 1,
attributes: {
title: "Mow the lawn",
completed: false
}
};
// Type string
const title = getAttribute(todo, "title");
// Type boolean
const completed = getAttribute(todo, "completed");
Please note that it is not necessary to implement and use this prop
function in order to get the properties of an object. If you use object destructuring the end result is the same, including the type inference!
interface Todo {
id: number;
text: string;
completed: boolean;
}
const todo: Todo = {
id: 1,
text: "Buy milk",
completed: false
};
const {id, text, completed} = todo;
Hi Marius,
Thank you very much for this course (one which is finally didactic and understandable). I tried to convert your example using a curried function
interface Todo {
id: number,
text: string,
done: boolean
}
const todo: Todo = {
id: 1,
text: 'learn TS',
done: false
}
const prop =
<T>(obj: T) =>
<K extends keyof T>(key: K) =>
obj[key]
const id = prop(todo)('id')
document.body.innerHTML = `Todo ${id}`
It works pretty nice but what if I want to swap the key and the object (like Ramda's prop function)? I tried but K
is declared before T
and I'm lost there.
@Filipe: Glad you liked the course! Take a look at this version and see if it works for you:
interface Todo {
id: number,
text: string,
done: boolean
}
const todo: Todo = {
id: 1,
text: 'learn TS',
done: false
}
const prop =
<T extends string>(key: T) =>
<U extends { [P in T]: U[T] }>(value: U) =>
value[key]
const getID = prop('id')
const id = getID(todo)
document.body.innerHTML = `Todo ${id}`
would doing this be "wrong", seems to work. I'm assuming we don't have to because typescript infers this?
function prop<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const id = prop<Todo, 'id'>(todo, "id"); // added the generics here
@Dean: No, that's not wrong at all! You're explicitly specifying the type arguments for the prop
function call that TypeScript already infers for you. There's no harm in that, but since TypeScript is doing type inference here, I would recommend to leave out the explicit type arguments.