Typescript 객체의 인덱싱 된 멤버 유형을 적용 하시겠습니까?


290

Typescript 객체에 string-> string의 매핑을 저장하고 모든 키가 문자열에 매핑되도록하고 싶습니다. 예를 들면 다음과 같습니다.

var stuff = {};
stuff["a"] = "foo";   // okay
stuff["b"] = "bar";   // okay
stuff["c"] = false;   // ERROR!  bool != string

값이 문자열 (또는 모든 유형)이어야한다는 것을 강요 할 수있는 방법이 있습니까?

답변:


521
var stuff: { [key: string]: string; } = {};
stuff['a'] = ''; // ok
stuff['a'] = 4;  // error

// ... or, if you're using this a lot and don't want to type so much ...
interface StringMap { [key: string]: string; }
var stuff2: StringMap = { };
// same as above

13
이 구문에서 흥미로운 점은 키의 'string'이외의 모든 유형이 실제로 잘못되었다는 것입니다. JS 맵에 문자열이 명시 적으로 지정되어 있지만 구문이 약간 중복되는 것을 감안할 때 완벽하게 이해됩니다. 생성 된 코드의 일부로 키를 자동으로 강제 변환 할 수있는 방법을 추가하지 않는 한 단순히 값의 유형을 지정하는 '{} : string'과 같은 것이 더 단순 해 보입니다.
Armentage

43
number색인 유형으로도 허용
Ryan Cavanaugh

3
주목할 가치가 있습니다. 컴파일러는 키 유형이 아닌 값 유형 만 시행합니다. 당신은 물건을 할 수 있습니다 [15] = '무엇이든'그리고 그것은 불평하지 않을 것입니다.
amwinter

3
아니요, 키 유형을 적용합니다. myObject에 멋진 toString () 구현이 있어도 stuff [myObject] = 'whatever'를 수행 할 수 없습니다.
AlexG

7
@RyanCavanaugh type Keys = 'Acceptable' | 'String' | 'Keys'인덱싱 (키) 유형으로 사용할 수 있습니까?
calebboyd

131
interface AgeMap {
    [name: string]: number
}

const friendsAges: AgeMap = {
    "Sandy": 34,
    "Joe": 28,
    "Sarah": 30,
    "Michelle": "fifty", // ERROR! Type 'string' is not assignable to type 'number'.
};

여기서 인터페이스 AgeMap는 키를 문자열로, 값을 숫자로 적용합니다. 키워드 name는 모든 식별자가 될 수 있으며 인터페이스 / 유형의 구문을 제안하는 데 사용해야합니다.

비슷한 구문을 사용하여 객체에 공용체 유형의 모든 항목에 대한 키가 있도록 할 수 있습니다.

type DayOfTheWeek = "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday";

type ChoresMap = { [day in DayOfTheWeek]: string };

const chores: ChoresMap = { // ERROR! Property 'saturday' is missing in type '...'
    "sunday": "do the dishes",
    "monday": "walk the dog",
    "tuesday": "water the plants",
    "wednesday": "take out the trash",
    "thursday": "clean your room",
    "friday": "mow the lawn",
};

물론 이것을 일반적인 유형으로 만들 수도 있습니다!

type DayOfTheWeek = "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday";

type DayOfTheWeekMap<T> = { [day in DayOfTheWeek]: T };

const chores: DayOfTheWeekMap<string> = {
    "sunday": "do the dishes",
    "monday": "walk the dog",
    "tuesday": "water the plants",
    "wednesday": "take out the trash",
    "thursday": "clean your room",
    "friday": "mow the lawn",
    "saturday": "relax",
};

const workDays: DayOfTheWeekMap<boolean> = {
    "sunday": false,
    "monday": true,
    "tuesday": true,
    "wednesday": true,
    "thursday": true,
    "friday": true,
    "saturday": false,
};

10.10.2018 업데이트 : 아래의 @ dracstaxi의 답변을 확인하십시오 . 이제 Record대부분을 지원 하는 내장 유형 이 있습니다.

1.2.2020 업데이트 : 답변에서 사전 작성된 매핑 인터페이스를 완전히 제거했습니다. @ dracstaxi의 대답은 완전히 관련이 없습니다. 그래도 계속 사용하려면 편집 기록을 확인하십시오.


1
{[키 : 숫자] : T; } 내부적으로 객체의 키는 항상 문자열 이기 때문에 타입 안전하지 않습니다 -자세한 내용은 @tep의 질문에 대한 주석을 참조하십시오. 예를 들어 x = {}; x[1] = 2;Chrome에서 실행하면 Object.keys(x)[ "1"]이 JSON.stringify(x)반환되고 '{ "1": 2}'가 반환됩니다. typeof Number(예 : Infinity, NaN, 1e300, 999999999999999999999 등)의 코너 케이스는 문자열 키로 변환됩니다. x[''] = 'empty string';, 같은 문자열 키에 대한 다른 코너 케이스도주의하십시오 x['000'] = 'threezeros'; x[undefined] = 'foo'.
robocat

@robocat 이것은 사실이며,이 답변에서 숫자로 된 인터페이스를 제거하기 위해 편집을 계속했습니다. 궁극적으로 TypeScript는 기술적 으로 구체적 으로 키 수를 허용 하기 때문에 유지하기로 결정했습니다 . 말했듯이, 숫자로 색인 된 객체를 사용하기로 결정한 사람은 귀하의 의견을 보길 바랍니다.
샌디 포드

75

빠른 업데이트 : Typescript 2.1부터 Record<T, K>사전처럼 작동 하는 내장 유형 이 있습니다.

문서의 예 :

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

const names = { foo: "hello", bar: "world", baz: "bye" };
const lengths = mapObject(names, s => s.length);  // { foo: number, bar: number, baz: number }

에 대한 TypeScript 2.1 설명서 Record<T, K>

내가 이것을 사용하는 유일한 단점 {[key: T]: K}은 "키"대신에 어떤 종류의 키를 사용하는지에 대한 유용한 정보를 인코딩 할 수 있다는 것 {[prime: number]: yourType}입니다.

다음은 이러한 변환을 돕기 위해 작성한 정규식입니다. 레이블이 "키"인 경우 만 변환됩니다. 다른 라벨을 변환하려면 첫 번째 캡처 그룹을 변경하십시오.

찾기: \{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}

바꾸다: Record<$2, $3>


3
이것은 더 많은 투표를해야합니다-그것은 본질적으로 내 대답의 새로운 기본 버전입니다.
Sandy Gifford

레코드가 {}? 로 컴파일됩니까 ?
Douglas Gaskell

@DouglasGaskell 타입은 컴파일 된 코드에 존재하지 않습니다. Record(Javascript와 달리 Map)는 객체 리터럴의 내용을 적용하는 방법 만 제공합니다. 당신은 할 수 new Record...const blah: Record<string, string>;로 컴파일합니다 const blah;.
샌디 포드

이 답변이 저에게 얼마나 도움이되었는지 상상할 수 없습니다. 정말 감사합니다 :)
Federico Grandi

문자열 조합도 Records 에서 작동한다는 점을 언급 할 가치가 있습니다. const isBroken: Record<"hat" | "teapot" | "cup", boolean> = { hat: false, cup: false, teapot: true };
Sandy Gifford

10

@Ryan Cavanaugh의 대답은 완전히 괜찮으며 여전히 유효합니다. ES16이 대부분의 플랫폼에서 지원된다고 주장 할 수있을 때 16 년 가을부터 추가 할 가치가 있습니다. 일부 키와 데이터를 연결해야 할 때마다 Map을 사용하는 것이 좋습니다.

우리가 글을 쓸 때 우리 let a: { [s: string]: string; }는 타입 스크립트가 컴파일 된 후에 타입 데이터와 같은 것이 없다는 것을 기억할 필요가 있습니다. 오직 컴파일에만 사용됩니다. 그리고 {[s : 문자열] : 문자열; }는 {}로 컴파일됩니다.

그것은 당신이 다음과 같은 것을 쓸지라도 :

class TrickyKey  {}

let dict: {[key:TrickyKey]: string} = {}

심지어이는 (컴파일되지 않습니다 target es6, 당신은 얻을 것이다error TS1023: An index signature parameter type must be 'string' or 'number'.

따라서 실제로 잠재적 인 키로 문자열 또는 숫자로 제한되므로 여기에서 유형 검사를 시행하는 의미가 많지 않습니다. 특히 js가 숫자로 키에 액세스하려고하면 문자열로 변환한다는 점을 명심하십시오.

따라서 모범 사례는 키가 문자열 인 경우에도 Map을 사용하는 것으로 가정하므로 매우 안전합니다.

let staff: Map<string, string> = new Map();

4
이 답변이 게시되었을 때 이것이 가능한지 확실하지 않지만 오늘 할 수 있습니다 : let dict: {[key in TrickyKey]: string} = {}- TrickyKey문자열 리터럴 유형 (예 :) "Foo" | "Bar".
로마 스타 코프

개인적으로 나는 기본 타입 스크립트 형식을 좋아하지만 표준을 사용하는 것이 가장 좋습니다. 그것은 나에게 정말 사용할 수 없습니다 키 "의 이름을"문서화하는 방법을 제공하지만 :) "patientId"같은라는 키 만들 수 있습니다
코딩

이 답변은 절대적으로 유효하며 매우 좋은 지적이지만 기본 Map객체 를 고수하는 것이 거의 항상 낫다는 생각에 동의하지 않습니다 . 지도에는 추가 메모리 오버 헤드가 있으며 JSON 문자열로 저장된 모든 데이터에서 수동으로 인스턴스화해야합니다. 그것들은 종종 매우 유용하지만 타입을 얻기 위해 순수한 것은 아닙니다.
샌디 포드

7

인터페이스 정의

interface Settings {
  lang: 'en' | 'da';
  welcome: boolean;
}

키를 설정 인터페이스의 특정 키로 설정

private setSettings(key: keyof Settings, value: any) {
   // Update settings key
}

3

@ shabunc의 대답을 바탕으로 키 또는 값 (또는 둘 다)을 적용하려는 모든 것이 될 수 있습니다.

type IdentifierKeys = 'my.valid.key.1' | 'my.valid.key.2';
type IdentifierValues = 'my.valid.value.1' | 'my.valid.value.2';

let stuff = new Map<IdentifierKeys, IdentifierValues>();

정의 enum대신 사용하여 작동해야 type합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.