Typescript의 배열에서 작성하고 집합하는 2가지 방법

20435 단어 typescript
만약 우리가 간단한 인터페이스를 가지고 대상을 묘사한다면.
interface Person {
    name: string;
    age: number;
    address: string;
};

// This is a valid 'Person' since the object contains all of the
// keys from the interface above and the types allline up.
const person: Person = {name: "shane", age: 21, address: "England"}
그러나 때때로, 우리는 낡은 인터페이스의 기초 위에서 새로운 인터페이스를 파생시켜야 하지만, 일부 키에만 한정될 수도 있다.
반환된 데이터를 제한할 때 일반적으로 API 응답에서 이렇게 합니다.
간단한 예로, name 인터페이스의 agePerson 필드만 포함하는 새로운 인터페이스를 만드는 방법을 보여 줍니다.

새로운 유형을 파생시키다


물론 세 개의 필드만 있는 간단한 예에서는 다음과 같은 새 인터페이스를 만들어야 할 수도 있습니다.
interface Person2 {
    name: string;
    age: number;
};
그러나 인터페이스가 실제 코드 라이브러리에서 점점 커지는 것을 알고 있기 때문에 우리는 정말로 nameage 필드와 유형을 다시 사용하고 싶다.
한 가지 방법은 pick 보존할 필드를 저장하는 것이다
type Person2 = Pick<Person, "name" | "age">
이것은 바로 우리가 위에서 원하는 것입니다. Pick "name" | "age" 병합된 요소를 사용하여 새로운 맵 형식을 만들기 때문입니다.이는 수동으로 작성한 내용과 같습니다.
type Person2 = {[K in "name" | "age"]: Person[K]}
여기서 정말 중요한 부분은 하드코딩이 아닌 새로운 유형을 파생시키고 있다는 것이다.이것은 밑바닥 Person 인터페이스에 대한 후속 변경 사항이 우리의 전체 코드 라이브러리에 반영된다는 것을 의미한다.
예를 들어 userPerson 속성이 object 대신 string로 변경되면 이 변경은 새로운 Person2 유형으로 전파된다.

매핑 유형이 어마어마합니다.


위에서 보듯이 기존 유형에서 새로운 유형을 파생시키는 것은 매우 강력한 개념이다. Typescript에 대한 지식이 증가함에 따라 점점 더 많은 새로운 유형을 사용할 것이다.
현재, 위의 코드 예시를 돌이켜 보면, 유니버설 "name" | "age" 을 동적 그룹으로 바꾸려면 어떤 일이 일어날까요?
하나의 키를 문자열로 하고, 이 키만 포함하는 대상을 되돌려 주는 함수를 상상할 수 있습니다.
// Here's the goal: we want Typescript
// to know that `p` here is not only a subset of `Person`
// but also it ONLY contains the keys "name" & "age"
const p = getPerson(["name", "age"]);
첫 번째 방법은 필드에 string[]를 받아들이고 되돌아오는 것일 수 있다Partial<Person>
function getPerson(fields: string[]): Partial<Person> {
   // implementation omitted for brevity
   return {} as any;
}
다음과 같은 두 가지 주요 문제가 있습니다.
  • (fields 매개 변수는 여기에 모든 문자열을 포함할 수 있지만, Person 인터페이스의 키만 허용할 수 있도록 범위를 좁히고 싶습니다.
  • Partial<Person>의 반환 형식은 모든 필드를 선택할 수 있기 때문에 유형 정보를 잃어버릴 수 있습니다.
  • 하드 코드


    이 두 문제를 어떻게 해결하는지 이해하기 위해서 함수 서명을 다시 써서 우리가 예시에서 원하는 유형을 완전히 포함하도록 합니다.
    function getPerson(fields: ("name" | "age")[]): Pick<Person, "name" | "age"> {
       // implementation omitted for brevity
       return {} as any;
    }
    
    지금은 표시 수프 같지만, 우선 fields 파라미터를 보면, 이것은 우리가 원하는 string[] (이 유형은 모든 문자열을 포함하기 때문에) 이 아니라, 실제로는 '사람에게 존재하는 키 그룹' 일 뿐이다.
    이 모든 것은 Typescript가 어떻게 연합 유형을 만들고 사용하는지, 그리고 그것들을 그룹 유형과 결합시키는지 이해하는 데 관한 것이다.
    // This is a "union" of 3 string literals
    type KeyUnion = ("name" | "age" | "person");
    
    // This is an Array type, where it's elements are 
    // restricted to those that are found in the union 
    // "name" | "age" | "person"
    type KeyArray = ("name" | "age" | "person")[];
    
    // Sometimes it's easier to understand in this style
    type KeyArray = Array<"name" | "age" | "person">;
    
    // Finally you can substitute the strings for the
    // type alias created above
    type Keys = KeyUnion[]
    
    일부 기본적인 연합/수조 유형에 대해 말하자면, 이것은 아주 좋은 입문 지식이다. 다음 업무의 원인을 이해하기 위해 이 위치가 필요하다.
    그러나 현재, 우리는 하드코딩 문자열의 텍스트를 완전히 피하기를 바란다.
    인터페이스의 키를 인용하는 문자열 목록을 유지하는 것은 매우 번거롭다. 다행히도 Typescript는 많은 편리한 유형이 우리를 돕는다.
    // Before: hard-coded keys
    type KeyUnion = ("name" | "age" | "person");
    
    // After: a 'derived' union type based on 
    // the Person interface
    type KeyUnion = keyof Person;
    
    비록 이 두 개는 등효이지만 어느 것이 가장 좋은지 잘 안다.따라서 이 부분을 함수에 적용하면 다음과 같은 결과를 얻을 수 있습니다.
    function getPerson(fields: (keyof Person)[]): Pick<Person, keyof Person> {
       // implementation omitted for brevity
       return {} as any;
    }
    

    아직.


    이것은 함수 호출 사이트에 형식 보안을 제공합니다. 인터페이스에 존재하지 않는 키 호출을 사용할 수 없습니다. getPerson
    // Type 'string' is not assignable to type '"name" | "age" | "address"'
    getPerson(["name", "age", "location"])
    
    위에서 말한 바와 같이 TS는 이러한 상황을 정확하게 방지했다. 왜냐하면 수조의 세 번째 요소는 Person의 유효한 키가 아니기 때문이다.
    그러나 반환 유형이 닫혔습니다.
    만약 우리가 mapped types로 잠시 돌아간다면, 우리는 왜 <Pick, keyof Person>가 우리가 원하는 것이 아닌지 쉽게 알게 될 것이다.
    // this creates a new type with ALL the keys in Person
    Pick<Person, keyof Person>
    
    // it's exactly the same as this, it's a 1:1 mapping
    {[K in keyof Person]: Person[K]}
    
    // Or, for even more clarity (not valid typescript though), it's like doing this
    {[for K in "name" | "age" | "person"]: Person[K]}
    
    그래서 우리는 여전히 Pick을 원하지만, 전송된 키 그룹을 바탕으로 하나의 연합 유형을 만들어야 한다. 기본적으로 다음과 같은 내용이 필요하다.
    const keys = ["name"]
    // -> should produce Pick<Person, "name">
    
    const keys = ["name", "age"]
    // -> should produce Pick<Person, "name" | "age">
    
    const keys = ["name", "age", "address"]
    // -> should produce Pick<Person, "name" | "age" | "address">
    

    그룹에서 집합을 만드는 첫 번째 방법


    독립된 예로 만약에 우리가 이런 유형의 단언을 한다면 우리는 다음과 같은 내용에서 문자열 텍스트의 합집합을 쉽게 얻을 수 있다.
    const keys: (keyof Person)[] = ["name", "age"]
    
    (keyof Person)[]를 사용하여 유형을 제한하는 것은 string[]뿐만 아니라 Typescript에 하나의 병합을 파생시킬 수 있음을 의미한다.
    // creates the union ("name" | "age")
    type Keys = typeof keys[number]
    
    사실이 증명하듯이, 모든 수조에서 typeof keys[number] 사용하면 Typescript로 하여금 모든 가능한 유형을 포함하는 합집합을 생성하게 할 것이다.
    우리의 예에서, 우리는 그룹이 인터페이스에서 온 키만 포함할 수 있다고 지적했기 때문에, Typescript는 이 정보를 사용하여 우리에게 필요한 연합을 제공한다.
    명확하게 보기 위해서 아래의 방법은 통하지 않는다
    const keys = ["name", "age"]
    
    // only creates the union `string` since there's no 
    // type assertion after `keys` 
    type Keys = typeof keys[number]
    

    수조에서 합집합을 만드는 두 번째 방법


    Typescript의 연합 유형은 이렇게 많은 고급 용례를 해제하는 관건이기 때문에 시종일관 그것들을 사용하는 방법을 모색할 필요가 있다.
    위에서 우리의 기능을 완성하기 위해서 우리는 실제적으로 첫 번째 예시를 사용할 것이다. 그러나 유사한 상황에 부딪히면 다른 기교를 이해하는 것이 가치가 있다.

    상수 단언


    비교적 새로운 특성을 사용하여 TS가 배열(및 기타 유형)을 텍스트(또는 읽기 전용) 유형으로 간주하도록 지시할 수 있습니다.
    보시다시피Typescript는 다음keys 변수의 유형string[]을 기술적으로 정확하게 추정하지만, 우리의 용례에 있어서, 이것은 우리가 그것으로부터 연합을 만들면 가치 있는 정보를 잃게 된다는 것을 의미합니다.
    // to TS, this is just `string[]`...
    const keys = ["name", "age"];
    
    // ... which means this union 
    // type ends up with just `string`, 
    // which includes every single string ever :(
    type Keys = typeof keys[number];
    
    그러나 aconst assertion를 사용하면 우리는 매우 다른 결과를 얻었다.너는 기본적으로 Typescript에 어떤 문자 형식도 확대할 수 없다고 말하고 있다.
    그래서 "name""name"가 아니라 string로 유지된다. 이것은 우리가 이전의 예로 돌아가면 Typescript에 모든 요소를 합쳐서 만들 수 있음을 의미한다.
    // now, TS thinks this is `readonly ["name", "age"]`
    const keys = ["name", "age"] as const;
    
    // this is now the union we wanted, "name" | "age"
    type Keys = typeof keys[number];
    

    구현 완료


    마지막으로, 우리는 이 지식들을 실천에 옮깁시다.
    위에서 말한 바와 같이, 우리는 Typescript를 사용하여 함수에 대한 주석을 하고자 한다. 그러면 문자열 그룹을 사용하여 함수를 호출할 때, 문자열이 인터페이스의 키 이름과 일치하는지 확인하고, 이 동적 값을 사용하여 원시 인터페이스에서 파생된 좁은 반환 형식을 만들 것이다.
    // only accept keys from `Person`
    // then use them to narrow a new type. 
    function getPerson<T extends (keyof Person)[]> (fields: T): Pick<Person, T[number]> {
       // implementation omitted for brevity
       return {} as any;
    }
    

    좋은 웹페이지 즐겨찾기