TypeScript의 속성을 사용하여 함수 개체 빌드


87

몇 가지 속성이있는 함수 개체를 만들고 싶습니다. 예를 들어 JavaScript에서는 다음을 수행합니다.

var f = function() { }
f.someValue = 3;

이제 TypeScript에서이 유형을 다음과 같이 설명 할 수 있습니다.

var f: { (): any; someValue: number; };

그러나 캐스트 없이는 실제로 만들 수 없습니다. 예 :

var f: { (): any; someValue: number; } =
    <{ (): any; someValue: number; }>(
        function() { }
    );
f.someValue = 3;

캐스트없이 어떻게 만들 수 있을까요?


2
이것은 귀하의 질문에 대한 직접적인 대답은 아니지만 속성이있는 함수 객체를 간결하게 만들고자하고 캐스팅에 문제가없는 사람에게는 Object-Spread 연산자 가 트릭을 수행하는 것 같습니다 var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };.
Jonathan

답변:


31

따라서 요구 사항이 단순히 캐스트없이 해당 함수를 "f"에 빌드하고 할당하는 것이라면 가능한 해결책은 다음과 같습니다.

var f: { (): any; someValue: number; };

f = (() => {
    var _f : any = function () { };
    _f.someValue = 3;
    return _f;
})();

기본적으로 자체 실행 함수 리터럴을 사용하여 할당이 완료되기 전에 해당 서명과 일치하는 개체를 "구성"합니다. 유일한 이상한 점은 함수의 내부 선언이 'any'유형이어야한다는 것입니다. 그렇지 않으면 컴파일러가 아직 객체에 존재하지 않는 속성에 할당하고 있다고 울었습니다.

편집 : 코드를 약간 단순화했습니다.


5
내가 이해할 수있는 한 이것은 실제로 유형을 확인하지 않으므로 .someValue는 본질적으로 무엇이든 될 수 있습니다.
shabunc 2015 년

1
TypeScript 2.0에 대한 2017/2018 기준으로 더 나은 / 업데이트 된 답변은 Meirion Hughes가 아래에 게시했습니다 : stackoverflow.com/questions/12766528/… .
user3773048

108

업데이트 :이 답변은 이전 버전의 TypeScript에서 최상의 솔루션 이었지만 최신 버전에서 더 나은 옵션을 사용할 수 있습니다 (다른 답변 참조).

허용되는 답변은 작동하며 일부 상황에서 필요할 수 있지만 객체를 구축하는 데 유형 안전을 제공하지 않는다는 단점이 있습니다. 이 기술은 정의되지 않은 속성을 추가하려고하면 최소한 유형 오류를 발생시킵니다.

interface F { (): any; someValue: number; }

var f = <F>function () { }
f.someValue = 3

// type error
f.notDeclard = 3

1
var f그 당시에는 someValue속성 이 없기 때문에 라인이 오류를 일으키지 않는 이유를 이해할 수 없습니다 .

2
@torazaburo는 캐스팅 할 때 확인하지 않기 때문입니다. 유형 검사를 수행하려면 다음을 수행해야합니다 var f:F = function(){}. 위의 예에서는 실패합니다. 이 답변은 할당 단계에서 유형 검사를 잃어 버리기 때문에 더 복잡한 상황에 적합하지 않습니다.
Meirion Hughes

1
맞지만 함수 표현식 대신 함수 선언으로 어떻게 수행합니까?
Alexander Mills

1
인터페이스 검사가 훨씬 더 엄격하고 필수 'someValue'속성이 없기 때문에 함수 캐스팅이 더 이상 컴파일되지 않기 때문에 최신 버전의 TS에서는 더 이상 작동하지 않습니다. 작동 무엇, 그러나,이다<F><any>function() { ... }
galenus

tslint : recommended를 사용할 때 typecast는 작동하지 않으며 사용해야하는 것은 다음과 같습니다. (함수를 대괄호로 묶어 표현식으로 만든 다음 F로 사용) :var f = (function () {}) as any as F;
NikhilWanpal

80

이것은 이제 쉽게 얻을 수 있습니다 (typescript 2.x) Object.assign(target, source)

예:

여기에 이미지 설명 입력

여기서 마법 Object.assign<T, U>(t: T, u: U)교차로 를 반환하도록 입력 된 것 입니다. T & U 입니다.

이것이 알려진 인터페이스로 해결되도록 강제하는 것도 간단합니다. 예를 들면 :

interface Foo {
  (a: number, b: string): string[];
  foo: string;
}

let method: Foo = Object.assign(
  (a: number, b: string) => { return a * a; },
  { foo: 10 }
); 

호환되지 않는 입력으로 인한 오류 :

오류 : foo : number는 foo : string에 할당 할 수 없습니다.
오류 : number는 string []에 할당 할 수 없습니다 (반환 유형).

주의 : 이전 브라우저를 대상으로하는 경우 Object.assign 을 폴리 필해야 할 수 있습니다 .


1
환상적입니다, 감사합니다. 2017 년 2 월 현재 가장 좋은 답변입니다 (Typescript 2.x 사용자의 경우).
Andrew Faulkner

47

TypeScript는 선언 병합을 통해이 경우를 처리하도록 설계되었습니다 .

함수를 만든 다음 함수에 속성을 추가하여 함수를 확장하는 JavaScript 방식에 대해서도 잘 알고있을 수 있습니다. TypeScript는 선언 병합을 사용하여 형식이 안전한 방식으로 이와 같은 정의를 작성합니다.

선언 병합을 통해 무언가가 함수이자 네임 스페이스 (내부 모듈)라고 말할 수 있습니다.

function f() { }
namespace f {
    export var someValue = 3;
}

이것은 타이핑을 보존하고 우리가 f()f.someValue. .d.ts기존 JavaScript 코드 용 파일을 작성할 때 declare다음을 사용하십시오 .

declare function f(): void;
declare namespace f {
    export var someValue: number;
}

함수에 속성을 추가하는 것은 TypeScript에서 종종 혼란 스럽거나 예상치 못한 패턴이므로 피하려고하지만 이전 JS 코드를 사용하거나 변환 할 때 필요할 수 있습니다. 이것은 내부 모듈 (네임 스페이스)을 외부와 혼합하는 것이 적절한 유일한 경우 중 하나입니다.


답변에서 주변 모듈을 언급하도록 찬성합니다. 이것은 기존 JS 모듈을 변환하거나 주석을 달 때 매우 일반적인 경우입니다. 정확히 내가 찾고있는 것!
Nipheris

아름다운. ES6 모듈과 함께 사용하려면 function hello() { .. } namespace hello { export const value = 5; } export default hello;Object.assign 또는 유사한 런타임 해킹보다 훨씬 깔끔한 IMO를 수행 할 수 있습니다 . 런타임, 유형 어설 션, 아무것도 없습니다.
skrebbel

이 대답은 훌륭하지만 Symbol.iterator와 같은 Symbol을 함수에 어떻게 연결합니까?
kzh

위의 링크가 더 이상 작동하지 않습니다. 올바른 것은 typescriptlang.org/docs/handbook/declaration-merging.html
iJungleBoy

선언 병합으로 인해 상당히 불안정한 JavaScript가 생성된다는 것을 알아야합니다. 간단한 속성 할당에 과도하게 보이는 추가 폐쇄를 추가합니다. 그것은 매우 비 자바 스크립트 방식입니다. 또한 함수가 가장 먼저 필요하다는 것을 확신하지 않는 한 결과 값이 반드시 함수가 아닌 종속성 순서 문제를 소개 합니다 .
John Leidegren


2

바로 가기로 [ 'property'] 접근자를 사용하여 객체 값을 동적으로 할당 할 수 있습니다.

var f = function() { }
f['someValue'] = 3;

이것은 유형 검사를 우회합니다. 그러나 동일한 방식으로 의도적으로 속성에 액세스해야하기 때문에 매우 안전합니다.

var val = f.someValue; // This won't work
var val = f['someValue']; // Yeah, I meant to do that

그러나 실제로 속성 값에 대한 유형 검사를 원하면 작동하지 않습니다.


1

업데이트 된 답변 : 교차로 유형 추가 이후 & 두 개의 추론 된 유형을 즉석에서 "병합"할 수 있습니다.

다음은 일부 객체의 속성을 읽고 객체 from위에 복사 하는 일반 도우미입니다 onto. 동일한 객체를 반환 onto하지만 두 속성 집합을 모두 포함하는 새 유형을 사용하여 런타임 동작을 올바르게 설명합니다.

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}

이 저수준 도우미는 여전히 유형 어설 션을 수행하지만 설계 상 유형 안전합니다. 이 도우미를 사용하면 전체 형식 안전성으로 OP의 문제를 해결하는 데 사용할 수있는 연산자가 있습니다.

interface Foo {
    (message: string): void;
    bar(count: number): void;
}

const foo: Foo = merge(
    (message: string) => console.log(`message is ${message}`), {
        bar(count: number) {
            console.log(`bar was passed ${count}`)
        }
    }
);

TypeScript 플레이 그라운드에서 사용해 보려면 여기를 클릭하십시오 . foo유형 Foo이되도록 제한 했으므로의 결과 merge는 완전해야 Foo합니다. 따라서로 이름 bar을 바꾸면 bad유형 오류가 발생합니다.

NB 그러나 여기에는 여전히 하나의 유형 구멍이 있습니다. TypeScript는 유형 매개 변수를 "함수가 아님"으로 제한하는 방법을 제공하지 않습니다. 따라서 혼란스럽고 함수를에 두 번째 인수로 전달할 수 있으며 merge작동하지 않습니다. 따라서 이것이 선언 될 수있을 때까지 런타임에 포착해야합니다.

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    if (typeof from !== "object" || from instanceof Array) {
        throw new Error("merge: 'from' must be an ordinary object");
    }
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}

1

오래된 질문이지만 3.1로 시작하는 TypeScript 버전의 경우 함수 선언이나 const변수에 대한 키워드를 사용하는 한 일반 JS 에서처럼 속성 할당을 수행 할 수 있습니다 .

function f () {}
f.someValue = 3; // fine
const g = function () {};
g.someValue = 3; // also fine
var h = function () {};
h.someValue = 3; // Error: "Property 'someValue' does not exist on type '() => void'"

참조온라인 예 .


0

이것은 강력한 타이핑에서 출발하지만 할 수 있습니다.

var f: any = function() { }
f.someValue = 3;

이 질문을 발견했을 때와 같이 억압적인 강력한 타이핑을 피하려는 경우. 슬프게도 이것은 TypeScript가 완벽하게 유효한 JavaScript에서 실패하는 경우이므로 TypeScript에 백 오프하도록 지시해야합니다.

"당신 JavaScript는 완벽하게 유효한 TypeScript입니다"는 거짓으로 평가됩니다. (참고 : 0.95 사용)

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