interface IPerson {
  name: string;
  age: number;
}

const person: IPerson = {
  name: 'malza',
  age: 29,
};

// keyof 키워드는 type 앞에 붙여서 새로운 type으로 나오는데 그 type은
// key 의 이름으로 즉 IPerson 의 key 들인 "name" 또는 "age" 로 나온다. 
// "name" | "age" 의 union 타입으로 나온 것이다.

// type Keys = keyof IPerson;
// const keys: Keys = 'age';
// 또는
// const keys: Keys = 'name'; 으로 자동 완성 되는 것을 볼 수 있다.

// 현재 string or number 로 받아지기 때문에 형을 확정 짓고 싶다.
// function getProp(obj: IPerson, key: "name" | "age"): string | number {
//   return obj[key];
// }

// function getProp(obj: IPerson, key: keyof IPerson): IPerson[keyof IPerson] {
//   return obj[key];
// }

// IPerson[keyof IPerson]을 해석 해 보면
// IPerson[keyof IPerson] => IPerson["name" | "age"]
// => IPerson["name"] | IPerson["age"]
// => string | number
// 아직은 하나로 확정이 나지 않았다.

// function getProp<T>(obj: T, key: keyof T): T[keyof T] {
//   return obj[key];
// }

// extends 키워드를 통해 "name" 이나 "age" 만 넣을 수 있도록 K를 제한한다.
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

getProp(person, 'name'); // string 형으로 정확하게 받을 수 있다.
getProp(person, 'age'); // 역시 자동완성 되면서 error를 사전에 차단할 수 있다.

function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
  obj[key] = value;
}

//setProp(person, 'name', 39); //error!
setProp(person, 'name', 'malza'); //good!
console.log(person); // { name: 'malza', age: 29 }