Chcę utworzyć obiekt funkcji, który również ma pewne właściwości. Na przykład w JavaScript zrobiłbym:
var f = function() { }
f.someValue = 3;
Teraz w TypeScript mogę opisać ten typ jako:
var f: { (): any; someValue: number; };
Jednak tak naprawdę nie mogę go zbudować bez konieczności rzucania. Jak na przykład:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
Jak zbudowałbyś to bez obsady?
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
.Odpowiedzi:
Więc jeśli wymaganiem jest po prostu zbudowanie i przypisanie tej funkcji do "f" bez rzutowania, oto możliwe rozwiązanie:
var f: { (): any; someValue: number; }; f = (() => { var _f : any = function () { }; _f.someValue = 3; return _f; })();
Zasadniczo używa samowykonującego się literału funkcji do „konstruowania” obiektu, który będzie pasował do tego podpisu przed wykonaniem przypisania. Jedyną dziwnością jest to, że wewnętrzna deklaracja funkcji musi być typu „any”, w przeciwnym razie kompilator wykrzykuje, że przypisujesz właściwość, która jeszcze nie istnieje w obiekcie.
EDYCJA: Uprościłem nieco kod.
źródło
Aktualizacja: ta odpowiedź była najlepszym rozwiązaniem we wcześniejszych wersjach TypeScript, 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ę wadę, że nie zapewnia bezpieczeństwa typu dla budowy obiektu. Ta technika przynajmniej zgłosi 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
źródło
var f
linia nie powoduje błędu, skoro w tym czasie nie masomeValue
właściwości.var f:F = function(){}
co zakończy się niepowodzeniem w powyższym przykładzie. Ta odpowiedź nie jest świetna w przypadku bardziej skomplikowanych sytuacji, ponieważ tracisz sprawdzanie typu na etapie przypisywania.<F><any>function() { ... }
var f = (function () {}) as any as F;
Jest to teraz łatwo osiągalne (maszynopis 2.x) z
Object.assign(target, source)
przykład:
Magia polega na tym, że
Object.assign<T, U>(t: T, u: U)
jest wpisywany w celu zwrócenia przecięciaT & U
.Wymuszenie, że rozwiąże to problem na znanym interfejsie, jest również proste. Na przykład:
interface Foo { (a: number, b: string): string[]; foo: string; } let method: Foo = Object.assign( (a: number, b: string) => { return a * a; }, { foo: 10 } );
które błędy spowodowane niezgodnym pisaniem:
Uwaga: w przypadku kierowania na starsze przeglądarki może być konieczne wypełnienie kodu Object. assign .
źródło
TypeScript jest przeznaczony do obsługi tego przypadku poprzez scalanie deklaracji :
Łączenie deklaracji pozwala nam powiedzieć, że coś jest zarówno funkcją, jak i przestrzenią nazw (moduł wewnętrzny):
function f() { } namespace f { export var someValue = 3; }
To zachowuje pisanie i pozwala nam pisać zarówno
f()
if.someValue
. Podczas pisania.d.ts
pliku dla istniejącego kodu JavaScript użyjdeclare
: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 TypeScript, więc staraj się tego unikać, ale może być konieczne podczas używania lub konwertowania starszego kodu JS. To jeden z nielicznych przypadków, w których należałoby mieszać moduły wewnętrzne (przestrzenie nazw) z zewnętrznymi.
źródło
function hello() { .. }
namespace hello { export const value = 5; }
export default hello;
IMO, jest to znacznie czystsze niż Object. assign lub podobne hacki uruchomieniowe. Bez środowiska uruchomieniowego, bez asercji typu, bez niczego.Nie mogę powiedzieć, że jest to bardzo proste, ale na pewno jest 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 zaciekawiło Cię to z mojej istoty w wersji TypeScript / JavaScript programu
Optional
źródło
Jako skrót możesz dynamicznie przypisać wartość obiektu za pomocą metody dostępu ['property']:
var f = function() { } f['someValue'] = 3;
To omija sprawdzanie typu. Jest to jednak dość bezpieczne, ponieważ musisz celowo uzyskać dostęp do nieruchomoś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 naprawdę chcesz, aby typ sprawdzał wartość właściwości, to nie zadziała.
źródło
Zaktualizowana odpowiedź: od czasu dodania typów skrzyżowań poprzez
&
, możliwe jest „scalenie” dwóch wywnioskowanych typów w locie.Oto ogólny pomocnik, który odczytuje właściwości jakiegoś obiektu
from
i kopiuje je na obiektonto
. Zwraca ten sam obiekt,onto
ale z nowym typem, który zawiera oba zestawy właściwości, więc poprawnie opisuje zachowanie w czasie wykonywania: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 pomocnik niskiego poziomu nadal wykonuje asercję typu, ale zgodnie z projektem jest bezpieczny dla typu. Mając tego pomocnika, 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ć to w TypeScript Playground . Zauważ, że ograniczyliśmy się
foo
do typuFoo
, więc wynikmerge
musi być kompletnyFoo
. Więc jeśli zmienisz nazwębar
nabad
, pojawi się błąd typu.Uwaga: jest tu jednak jeszcze jeden rodzaj otworu. TypeScript nie zapewnia sposobu na ograniczenie parametru typu, aby nie był on funkcją. Więc możesz się pogubić i przekazać swoją funkcję jako drugi argument
merge
, a to nie zadziała. Więc dopóki nie zostanie to zadeklarowane, musimy to 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; }
źródło
Stare pytanie, ale w przypadku wersji TypeScript zaczynających się od 3.1 możesz po prostu przypisać właściwość tak, jak w zwykłym JS, o ile używasz deklaracji funkcji lub
const
słowa kluczowego dla swojej zmiennej: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'"
Odniesienie i przykład online .
źródło
To odbiega od silnego pisania, ale możesz to zrobić
var f: any = function() { } f.someValue = 3;
jeśli próbujesz obejść uciążliwe, mocne pisanie, tak jak ja, kiedy znalazłem to pytanie. Niestety jest to przypadek, w którym TypeScript nie działa na doskonale poprawnym JavaScript, więc musisz powiedzieć TypeScript, aby się wycofał.
„Twój JavaScript jest całkowicie poprawny TypeScript” przyjmuje wartość fałsz. (Uwaga: przy użyciu 0,95)
źródło