TypeScript에서 주어진 유형의 값을 가진 객체 유형의 키를 가져오는 방법은 무엇입니까?
나는 값이 문자열인 유형의 키로 구성된 유형을 만들려고 노력해 왔다. 의사 코드에서는 그럴 것이다.
제가 생각할 수 있는 유일한 방법은 두 단계입니다:
// a mapped type that filters out properties that aren't strings via a conditional type
type StringValueKeys<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };
// all keys of the above type
type Key<T> = keyof StringValueKeys<T>;
하지만 TS 컴파일러는 내가 문자열이 아닌 키를 조건부 유형으로 설정하여 필터링했음에도 불구하고 그것은 단순히 동일하다고 말한다.
따라서 다음과 같은 예를 들어 여전히 이를 예로 들 수 있습니다:
interface Thing {
id: string;
price: number;
other: { stuff: boolean };
}
const key: Key<Thing> = 'other';
다른 두 키의 값이 문자열이 아니기 때문에 유일하게 허용되는 값이 실제로가 아닌 경우.
이를 기본적으로 지원하기 위한 기능 요청이 에 있습니다. 그러나 이 기능이 구현되기 전까지는 여러 가지 방법으로 자신만의 버전을 만들 수 있습니다.
한 가지 방법은 와 를 사용하는 것입니다:
type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];
그런 다음 속성이 일치하는 키를 꺼냅니다:
const key: KeysMatching<Thing, string> = 'other'; // ERROR!
// '"other"' is not assignable to type '"id"'
상세:
KeysMatching<Thing, string> ➡
{[K in keyof Thing]-?: Thing[K] extends string ? K : never}[keyof Thing] ➡
{
id: string extends string ? 'id' : never;
price: number extends string ? 'number' : never;
other: { stuff: boolean } extends string ? 'other' : never;
}['id'|'price'|'other'] ➡
{ id: 'id', price: never, other: never }['id' | 'price' | 'other'] ➡
'id' | never | never ➡
'id'
당신이 한 일은 다음과 같습니다:
type SetNonStringToNever<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };
정말로 문자열이 아닌 속성을 속성 값으로 바꾸는 것이었습니다. 열쇠에 손을 대지 않았어요. 여러분들은. 이 키는 의 키와 동일합니다. 그것과 가장 큰 차이점은 값이 아니라 키를 선택해야 한다는 것이다.
보충적인 답변:
버전 4.1 이후에는 대체 솔루션에 활용할 수 있습니다(핵심 논리는 jcalz의 논리와 다르지 않음). 소스 유형을 인덱싱하는 데 사용할 때 대상 유형에 할당할 수 있는 유형을 생성하지 않는 키를 필터링하고 나머지 키의 결합을 추출하기만 하면 됩니다:
type KeysWithValsOfType<T,V> = keyof { [ P in keyof T as T[P] extends V ? P : never ] : P };
interface Thing {
id: string;
price: number;
test: number;
other: { stuff: boolean };
}
type keys1 = KeysWithValsOfType<Thing, string>; //id -> ok
type keys2 = KeysWithValsOfType<Thing, number>; //price|test -> ok
에 의해 올바르게 언급된 바와 같이:
둘 다 문자열 키의 결합을 추출할 수 있습니다. 하지만, T 확장 키와 같이 더 복잡한 상황에서 사용해야 할 때는...<T, X> 그러면 TS는 솔루션을 "이해"할 수 없습니다.
위의 유형은 매핑된 유형과 함께 인덱싱되지 않고 대신 사용하기 때문에 컴파일러는 출력 유니언에 의해 인덱싱될 수 있다고 추론할 수 없습니다. 컴파일러가 이에 대해 확실히 하기 위해, 후자를 다음과 교차시킬 수 있다:
type KeysWithValsOfType<T,V> = keyof { [ P in keyof T as T[P] extends V ? P : never ] : P } & keyof T;
function getNumValueC<T, K extends KeysWithValsOfType<T, number>>(thing: T, key: K) {
return thing[key]; //OK
}
다른 사람들이 나와 같은 질문을 할까봐, 나는 리액트에서 유형 추론이 있는 일반 객체 속성으로 인덱싱하기 위해 이와 같은 패턴을 사용하려고 했지만 작동하지 않았다.
function ListWithSum<T>({
data,
value,
}: {
data: T
value: KeysMatching<T, number>
}) {
// 'item[value]' would not have type 'number', causing a type mismatch
const sum = data.reduce((total, item) => total + item[value], 0)
// ...
}
추가 유형 도입:
type PickKeysMatching<T, V> = {
[key in KeysMatching<T, V>]: V
}
그리고 그것을 구속하는 데 사용하면, 나는 안전하게 받침대에 색인을 넣을 수 있고, 유형에 맞게 정확하게 해결할 수 있다.
function ListWithSum<T extends PickKeysMatching<T, number>>({
data,
value,
}: {
data: T
value: KeysMatching<T, number>
}) {
// 'item[value]' is now a 'number'
const sum = data.reduce((total, item) => total + item[value], 0)
return (
<ul>
{data.map((item) => (
<li>{item[value]}</li>
))}
<li><b>Sum: {sum}</b></li>
</ul>
)
}
구성 요소를 사용할 때는 소품에 전달된 키에 대해서도 유형이 확인됩니다. 소품은 속성에 대한 키를 기대하기 때문에 패스하면 has type과 같은 오류가 발생합니다.
type Contract = { title: string; payment: number}
function Example(){
const contracts: Contract[] = [
{ title: 'Walking neighbourhood dogs', payment: 300 },
{ title: 'Built website for client', payment: 2000 },
{ title: 'Mowed parents lawn', payment: 50 },
]
return <ListWithSum data={contracts} value='payment' />
}
'개발하자' 카테고리의 다른 글
테라폼 보간에서 문자열에 하위 문자열이 포함되어 있는지 확인하는 방법은 무엇입니까? (0) | 2023.06.15 |
---|---|
Android Studio(설치되지 않음), Android Studio가 기계에 설치된 상태에서 Float Doctor를 실행하는 경우 (0) | 2023.06.15 |
주피터 노트북에 원시 파이썬 파일 생성 (0) | 2023.06.13 |
모든 장치에서 컨테이너 자동 확장 (0) | 2023.06.13 |
Is there a way to view cPickle or Pickle file contents without loading Python in Windows? (0) | 2023.06.12 |