Typescript : 매핑 된 유형의 인덱스 서명
어떻게 형이 걸릴 수 { 'k': number, [s: string]: any }
와 추상적 인 이상 'k'
과 number
? 해당 유형 T
을 T<'k', number>
제공 하는 유형 별칭을 갖고 싶습니다 .
다음 예를 고려하십시오.
function f(x: { 'k': number, [s: string]: any }) {} // ok
type T_no_params = { 'k': number, [s: string]: any }; // ok
type T_key_only<k extends string> = { [a in k]: number }; // ok
type T_value_only<V> = { 'k': V, [s: string]: any}; // ok
type T_key_and_index<k extends string, V> = { [a in k]: V, [s: string]: any };// ?
{ 'k': number, [s: string]: any}
함수의 매개 변수 유형으로 직접 사용하면f
작동합니다.- -alias 작업
[s: string]: any
에서 색인 된 부분 사용type
k extends string
intype
-alias 사용 도 작동합니다.- 결합하면
k extends string
과를[s: string]: any
동일에type
-alias, 나는 구문 분석 오류 수 (심지어 의미 론적 오류를, 심지어는 유효한 구문이 될 것 같지 않습니다).
여기에서 작동하는 것 같습니다.
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }
그러나 여기에서는 왜 추가 속성에 대해 불평하지 않는지 이해할 수 없습니다 (오른쪽에있는 유형은 추가 속성이있는 &
개체를 허용하지 않아야 함).
편집 :
&
집합 이론적 교차점과 유사하게 동작해야하는 교차 연산자 인이 답변에서 여러 번 언급되었습니다 . 그러나 다음 예제에서 보여주는 것처럼 추가 속성을 처리 할 때는 그렇지 않습니다.
function f(x: {a: number}){};
function g(y: {b: number}){};
function h(z: {a: number} & {b: number}){};
f({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {a: number}
g({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {b: number}
h({a: 42, b: 58}); // compiles!
이 예 {a: 42, b: 58}
에서는가 type {a: number}
도 아니고 type도 아닌 것처럼 보이지만 {b: number}
어떻게 든 교차점에서 끝납니다 {a: number} & {b: number}
. 그것은 집합 이론적 교차가 작동하는 방식이 아닙니다.
그것이 바로 제 &
제안이 저에게 의심스러워 보이는 이유 입니다. 누군가 매핑 된 유형과 "교차하는"유형 { [s: string]: any }
이 유형을 더 작게 만드는 대신 "더 크게"만들 수 있는 방법을 자세히 설명해 주시면 감사하겠습니다 .
나는 질문을 보았다
- Typescript의 매핑 된 유형에 대한 색인 서명
- 매핑 된 유형에 대한 인덱스 서명을 추가하는 방법
그러나 그것들은 비슷한 이름을 가지고 있음에도 불구하고 직접적인 관련이있는 것처럼 보이지 않았습니다.
답변
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }
당신이 추구하는 유형을 정의하는 올바른 방법입니다. 그러나 알아야 할 한 가지는 ( deprecated flag : keyofStringsOnly ) :
keyof 유형 연산자는 문자열을 반환 | 문자열 인덱스 서명이있는 유형에 적용될 때 문자열 대신 숫자입니다.
인덱스를 string
유형이 아닌 유형 으로 제한하는 방법을 모르겠습니다 string | number
. 실제로 인덱스 number
액세스를 허용 하는 string
것은 Javascript가 작동하는 방식 (항상 숫자를 문자열화할 수 있음)과 일치하기 때문에 합리적인 것으로 보입니다. 반면에 문자열 값으로 숫자 인덱스에 안전하게 액세스 할 수 없습니다.
&
이 항상 가능한 값의 설정 제한합니다 (또는 잎을 변경하지만, 확장하지 않음) - 타입 연산자는 이론적 교점을 설정하는 유사하게 작동합니다. 귀하의 경우 유형은 문자열과 유사하지 않은 키를 색인으로 제외합니다. 정확하게 unique symbol
는 인덱스로 제외 합니다.
Typescript가 함수 매개 변수를 처리하는 방식에서 혼란이 올 수 있다고 생각합니다. 명시 적으로 정의 된 매개 변수로 함수를 호출하는 것은 매개 변수를 변수로 전달하는 것과는 다르게 작동합니다. 두 경우 모두 Typescript는 모든 매개 변수가 올바른 구조 / 모양인지 확인하지만 후자의 경우 추가 소품을 허용하지 않습니다.
개념을 설명하는 코드 :
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V };
type WithNumber = HasKeyValue<"n", number>;
const x: WithNumber = {
n: 1
};
type T = keyof typeof x; // string | number
x[0] = 2; // ok - number is a string-like index
const s = Symbol("s");
x[s] = "2"; // error: cannot access via symbol
interface N {
n: number;
}
function fn(p: N) {
return p.n;
}
const p1 = {
n: 1
};
const p2 = {
n: 2,
s: "2"
};
fn(p1); // ok - exact match
fn(p2); // ok - structural matching: { n: number } present; additional props ignored
fn({ n: 0, s: "s" }); // error: additional props not ignore when called explictily
fn({}); // error: n is missing
편집하다
객체 리터럴-명시 적으로 어떤 모양의 객체를 만드는 const p: { a: number} = { a: 42 }
것은 Typescript에 의해 특별한 방식으로 처리됩니다. 일반적인 구조적 추론과는 반대로 유형이 정확히 일치해야합니다. 솔직히 말해서, 안전하지 않은 추가 캐스트가없는 추가 속성은 어쨌든 액세스 할 수 없기 때문에 이치에 맞습니다.
[...] 그러나 TypeScript는이 코드에 버그가있을 수 있다는 입장을 취합니다. 객체 리터럴은 다른 변수에 할당하거나 인수로 전달할 때 특별한 처리를 받고 과도한 속성 검사를받습니다. 객체 리터럴에 "대상 유형"에없는 속성이 있으면 오류가 발생합니다. [...] 이러한 검사를 처리하는 마지막 방법 중 하나는 약간 의외 일 수 있지만 객체를 다른 변수에 할당하는 것입니다.
TS 핸드북
이 오류를 해결하는 또 다른 옵션은 ...와 교차하는 것입니다 { [prop: string]: any }
.
추가 코드 :
function f(x: { a: number }) {}
function g(y: { b: number }) {}
function h(z: { a: number } & { b: number }) {}
f({ a: 42, b: 58 } as { a: number }); // compiles - cast possible, but `b` inaccessible anyway
g({ a: 42 } as { b: number }); // does not compile - incorrect cast; Conversion of type '{ a: number; }' to type '{ b: number; }' may be a mistake
h({ a: 42, b: 58 }); // compiles!
const p = {
a: 42,
b: 58
};
f(p); // compiles - regular structural typing
g(p); // compiles - regular structural typing
h(p); // compiles - regular structural typing
const i: { a: number } = { a: 42, b: 58 }; // error: not exact match
f(i); // compiles
g(i); // error
h(i); // error
교차로 연산자에 대한 추론 방법이 있습니다. 도움이 될 수 있습니다.
type Intersection = { a: string } & { b: number }
당신이 읽을 수있는 Intersection
"속성이있는 객체로 a
유형 string
및 속성 b
유형을 number
". 이 간단한 유형도 설명합니다.
type Simple = { a: string; b: number }
그리고 두 가지 유형이 호환됩니다. 거의 모든 용도로 하나를 다른 것으로 바꿀 수 있습니다.
이것이 HasKeyValue
정의하려는 유형과 실제로 동일한 이유를 설명하기를 바랍니다 .
T_key_and_index
작동하지 않는 이유 는 첫 번째 부분 인 [a in k]: V
은 매핑 된 유형을 정의하고 매핑 된 유형 의 정의에서 추가 속성을 가질 수 없기 때문입니다. 매핑 된 유형에 추가 속성을 추가해야하는 경우를 사용하여 유형 교차를& 만들 수 있습니다 .