Zbuduj obiekt funkcji z właściwościami w TypeScript

Chcę utworzyć obiekt funkcji, który również posiada pewne właściwości. Na przykład w JavaScript zrobiłbym:

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

Teraz w maszynopisie mogę opisać Typ tego jako:

var f: { (): any; someValue: number; };
Jednak nie mogę go zbudować, nie wymagając gipsu. Np.:
var f: { (): any; someValue: number; } =
    <{ (): any; someValue: number; }>(
        function() { }
    );
f.someValue = 3;
Jak byś to zbudował bez gipsu?
Author: Jonathan, 2012-10-07

8 answers

Więc jeśli wymóg jest po prostu zbudować i przypisać tę funkcję do " f " bez odlewu, oto możliwe rozwiązanie:

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

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

Zasadniczo używa samoczynnie wykonującej się funkcji do "konstruowania" obiektu, który będzie pasował do tej sygnatury przed wykonaniem przypisania. Jedyną dziwnością jest to, że wewnętrzna deklaracja funkcji musi być typu "any", w przeciwnym razie kompilator krzyczy, że przypisujesz właściwość, która jeszcze nie istnieje na obiekcie.

Edytuj: Uprościłem trochę kod.

 17
Author: nxn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-07 17:05:41

Update: ta odpowiedź była najlepszym rozwiązaniem we wcześniejszych wersjach maszynopisu, ale są lepsze opcje dostępne w nowszych wersjach (zobacz inne odpowiedzi).

Zaakceptowana odpowiedź działa i może być wymagana w niektórych sytuacjach, ale ma tę zaletę, że nie zapewnia bezpieczeństwa typu przy budowaniu obiektu. Ta technika spowoduje co najmniej błąd typu, jeśli spróbujesz dodać niezdefiniowaną właściwość.

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

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

// type error
f.notDeclard = 3
 81
Author: Greg Weber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-28 20:20:30

TypeScript jest przeznaczony do obsługi tej sprawy poprzez łączenie deklaracji:

Możesz być również zaznajomiony z praktyką JavaScript tworzenia funkcji, a następnie rozszerzania funkcji przez dodawanie właściwości do funkcji. TypeScript używa scalania deklaracji do tworzenia definicji w sposób bezpieczny dla typu.

Łączenie deklaracji pozwala nam powiedzieć, że coś jest zarówno funkcją, jak i przestrzenią nazw (wewnętrzna moduł):

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

Pozwala to na zapisanie zarówno f(), jak i f.someValue. Przy zapisie pliku .d.ts dla istniejącego kodu JavaScript, użyj declare:

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

Dodawanie właściwości do funkcji jest często mylącym lub nieoczekiwanym wzorcem w maszynopisie, więc staraj się go unikać, ale może być to konieczne podczas używania lub konwersji starszego kodu JS. Jest to jeden z niewielu przypadków, kiedy właściwe byłoby połączenie modułów wewnętrznych (przestrzeni nazw) z zewnętrznymi.

 38
Author: mk.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 16:42:25

Jest to teraz łatwo osiągalne (typescript 2.X) z Object.assign(target, source)

Przykład:

Tutaj wpisz opis obrazka

Magia polega na tym, że Object.assign<T, U>(...) jest wpisywana w celu zwrócenia przecięcia typu T & U.

Wymuszenie, że to rozwiązuje się do znanego interfejsu jest również proste:

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

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

Error: foo: number not assignable to Foo: string
Error: number not assignable to string [] (return type)

ZASTRZEŻENIE: Może być konieczne polyfill obiekt.przypisanie if do starszych przeglądarek.

 32
Author: Meirion Hughes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 07:57:36

Jako skrót możesz dynamicznie przypisać wartość obiektu za pomocą accesora ['property']:

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

To omija sprawdzanie typu. Jest to jednak dość bezpieczne, ponieważ musisz celowo uzyskać dostęp do właściwości w ten sam sposób:

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

Jeśli jednak chcesz, aby Typ sprawdzał wartość właściwości, to nie zadziała.

 2
Author: Rick Love,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-11 04:38:37

Zaktualizowana odpowiedź: od dodania typów przecięcia przez &, możliwe jest "łączenie" dwóch wnioskowanych typów w locie.

Oto ogólny helper, który odczytuje właściwości jakiegoś obiektu from i kopiuje je nad obiektem onto. Zwraca ten sam obiekt onto, ale z nowym typem, który zawiera oba zestawy właściwości, więc poprawnie opisuje zachowanie środowiska uruchomieniowego:

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

Ten niskopoziomowy Pomocnik nadal wykonuje twierdzenie typu, ale jest bezpieczny dla typu przez design. Z tym helperem mamy operatora, którego możemy użyć do rozwiązania problemu OP z pełnym bezpieczeństwem typu:

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}`)
        }
    }
);

Kliknij tutaj, aby wypróbować go w maszynopisie plac zabaw . Zauważ, że ograniczyliśmy {[7] } do typu Foo, więc wynik merge musi być zupełny Foo. Więc jeśli zmienisz nazwę bar na bad, otrzymasz błąd typu.

NB jest jeszcze jeden typ otworu tutaj, jednak. Maszynopis nie daje sposobu na ograniczenie parametr typu ma być "nie funkcją". Więc możesz się pomylić i przekazać swoją funkcję jako drugi argument merge, a to nie zadziała. Tak więc dopóki nie można tego zadeklarować, musimy go złapać w czasie wykonywania:

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
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 19:14:36

Nie mogę powiedzieć, że to bardzo proste, ale na pewno możliwe:

interface Optional {
  <T>(value?: T): OptionalMonad<T>;
  empty(): OptionalMonad<any>;
}

const Optional = (<T>(value?: T) => OptionalCreator(value)) as Optional;
Optional.empty = () => OptionalCreator();

Jeśli jesteś ciekawy to jest z mojego GISTA z wersją TypeScript / JavaScript Optional

 1
Author: thiagoh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 00:10:03

To odbiega od mocnego pisania, ale można to zrobić

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

Jeśli próbujesz ominąć opresyjne mocne pisanie, tak jak ja, gdy znalazłem to pytanie. Niestety jest to przypadek, że TypeScript zawodzi na doskonale poprawnym JavaScript, więc musisz powiedzieć TypeScript, aby się wycofać.

"You JavaScript is perfectly valid TypeScript" ocenia NA false. (Uwaga: użycie 0.95)

 0
Author: WillSeitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-21 15:59:26