Jess
Jess

Reputation: 650

TypeScript generic type infer

I have an object with some fields, and I'm going to implement an update function, which can modify value of existed key in my object. How can I restrict the function to only accept the existed keys in object, and the new value's type must be the same as the original value.

const defaultData = {
    name: '',
    count: 0,
    desc: '',
    visited: false,
};

type DataType = typeof defaultData;
type Keys = keyof DataType;

let data = Object.assign({}, defaultData);

// after tried, I found out this works. But not quite understand it
function updateData<T extends Keys, P extends DataType[T]>(k: T, value: P){
    data[k] = value;
}

updateData('name', 'hehe');
updateData('count', false);

I'm wondering whether this is the ONLY way to define the updateData function. What is P extends DataType[T] is? I tried to use <T extends Keys, P = DataType[T]>, but it is invalid.

Upvotes: 1

Views: 92

Answers (2)

georg
georg

Reputation: 215039

I guess you can do that in a more generic way, so that you don't have to write type DataType = typeof defaultData; etc for every data type you want to update.

function updater<T extends {}>(data: T) {
  return function <K extends keyof T>(key: K, value: T[K]) {
    data[key] = value
  }
}

This is a generic higher order function that creates an updater function for a specific data type. It can be used like this:

let myData = {
  name: '',
  count: 0,
};


let updateMyData = updater(myData);

updateMyData('name', 'hehe'); // ok
updateMyData('blag', 'hehe'); // fail
updateMyData('count', false); // fail

Play

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250266

Your option is an ok one, a simpler one would be to not use the P type argument at all as we don't care about the actual type passed in, we only care that is is compatible with DataType[T]:

function updateData<T extends Keys>(k: T, value: DataType[T]){
    data[k] = value;
}

Play

The reason P extends DataType[T] works while P = DataType[T] does not is that they do very different things. P extends DataType[T] means that P need to be a subtype of DataType[T]. P = DataType[T] means that if P can't be inferred, DataType[T] should be used (but P can be any type not necessarily a sub type of DataType[T])

Upvotes: 1

Related Questions