Zbudować obiekt funkcyjny o właściwościach na maszynie

głosy
46

Chcę utworzyć obiekt funkcyjny, który ma również pewne właściwości przechowywanych na nim. Na przykład w JavaScript Chciałbym zrobić:

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

Teraz w maszynopisie mogę opisać typ to jako:

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

Jednak nie mogę faktycznie zbudować go, bez konieczności obsady. Jak na przykład:

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

W jaki sposób można zbudować to bez gipsie?

Utwórz 07/10/2012 o 07:08
źródło użytkownik
W innych językach...                            


9 odpowiedzi

głosy
73

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

Przyjęte rozwiązanie działa i może być wymagany w niektórych sytuacjach, ale mają wadę nie zapewniając bezpieczeństwa typu dla budowania obiektu. Technika ta będzie co najmniej rzucać typu błędu, jeśli spróbujesz dodać niezdefiniowanej właściwości.

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

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

// type error
f.notDeclard = 3
Odpowiedział 05/09/2013 o 16:12
źródło użytkownik

głosy
34

Maszynopis został zaprojektowany do obsługi tej sprawy poprzez deklaracji łączenia :

może również zapoznać się z praktyką JavaScript tworzenia funkcji i rozszerzenie funkcji dodatkowo dodając właściwości na funkcję. Maszynopis używa deklaracji łączenie zbudować definicje jak to w bezpieczny sposób typu.

Deklaracja łączenie pozwala nam powiedzieć, że coś jest zarówno funkcja i nazw (moduł wewnętrzny):

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

Ten zachowuje pisanie i pozwala nam napisać zarówno f()i f.someValue. Pisząc .d.tsplik na istniejący kod JavaScript, użyj declare:

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

Dodawanie właściwości funkcji jest często mylące lub nieoczekiwany wzór w maszynopisie, więc staram się go unikać, ale może być konieczne w przypadku korzystania lub przekształcania starszego kodu JS. Jest to jedno z niewielu godzinach odpowiednie byłoby wymieszać moduły wewnętrzne (Namespaces) z zewnętrznym.

Odpowiedział 28/10/2015 o 13:47
źródło użytkownik

głosy
27

To jest teraz łatwo osiągalne (maszynopis 2.x) z Object.assign(target, source)

przykład:

wprowadzić opis obrazu tutaj

Magia jest to, że Object.assign<T, U>(...)jest wpisany do powrotu do przecięcia typu T & U.

Egzekwowanie że ten postanawia znanego interfejsu jest prosta:

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

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

Błąd: bla: liczba nie do wykorzystania foo string
błąd: liczba nie przypisane do łańcucha [] (typ zwrotny)

uwaga: może trzeba PolyFill Object.assign jeśli targetting starszych przeglądarek.

Odpowiedział 25/01/2017 o 13:43
źródło użytkownik

głosy
17

Więc jeśli wymóg jest po prostu budować i przypisać tę funkcję na „F” bez obsady, tutaj jest możliwe rozwiązanie:

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

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

Zasadniczo używa samodzielne wykonywanie funkcji dosłownego do „skonstruować” obiekt, który będzie pasował ten podpis przed przypisaniem, jest wykonywana. Jedynym niesamowitość jest, że wewnętrzna deklaracja funkcji musi być typu „każdy”, w przeciwnym razie kompilator krzyczy, że jesteś przypisywanie do właściwości, której nie ma jeszcze na obiekcie.

EDIT: Uproszczony kod trochę.

Odpowiedział 07/10/2012 o 17:33
źródło użytkownik

głosy
1

Nie mogę powiedzieć, że jest to bardzo proste, ale jest to 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 masz ciekawy jest z GIST kopalni z wersją maszynopis / JavaScript zOptional

Odpowiedział 07/02/2018 o 23:57
źródło użytkownik

głosy
1

Uaktualniona odpowiedź: ponieważ dodanie typów przecięcia poprzez &możliwe jest, aby „połączyć” dwa przypuszczalne typy na bieżąco.

Oto ogólny pomocnik, który odczytuje właściwości jakiegoś przedmiotu fromi kopiuje je na obiekcie onto. Zwraca ten sam obiekt onto, ale z nowego typu, który zawiera oba zestawy właściwości, więc prawidłowo opisujące zachowanie środowiska wykonawczego:

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 niski poziom pomocnik ma jeszcze przeprowadzić typu twierdzenie, ale jest bezpieczny typu projektem. Z tego pomocnika w miejscu, mamy operatora, który możemy wykorzystać w celu rozwiązania problemu OP z pełną bezpieczeństwa 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 go wypróbować w maszynopisie Playground . Należy pamiętać, że mamy ograniczone foodo być typu Foo, więc wynik mergemusi być kompletny Foo. Więc jeśli przemianować barna badnastępnie pojawi się błąd typu.

NB Jest jeszcze jeden rodzaj dziura tu jednak. Maszynopis nie przewiduje sposobu Aby ograniczyć parametr typu być „nie jest funkcją”. Więc można się pomylić i przekazać swoją funkcję jako drugi argument merge, a to nie będzie działać. Więc dopóki ta może być uznana, 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;
}
Odpowiedział 10/03/2016 o 19:14
źródło użytkownik

głosy
1

Jako skrót można dynamicznie przypisać wartość obiektu za pomocą [ „”] akcesor właściwości:

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

Ten omija sprawdzanie typu. Jednak jest to bardzo bezpieczny, bo trzeba celowo dostępu 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

Jednakże, jeśli naprawdę chcesz rodzaj sprawdzanie wartości nieruchomości, to nie będzie działać.

Odpowiedział 11/10/2012 o 05:38
źródło użytkownik

głosy
0

Stare pytanie, ale dla wersji maszynopisu zaczynające się od 3.1, można po prostu zrobić przypisanie własności, tak jak w zwykłym JS, tak długo jak używasz deklarację funkcji lub constsłowa kluczowego dla 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'"

Odniesienia i przykład w Internecie .

Odpowiedział 26/11/2018 o 21:38
źródło użytkownik

głosy
0

To odbiega od silnego typowania, ale można to zrobić

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

jeśli starają się obejść ucisku silne wpisywanie jak ja kiedy znalazłem na to pytanie. Niestety jest to przypadek maszynopis nie na doskonale poprawny JavaScript więc trzeba powiedzieć maszynopis się wycofać.

„Ty JavaScript jest całkowicie poprawny maszynopis” wartość false. (Uwaga: przy użyciu 0,95)

Odpowiedział 21/02/2014 o 16:59
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more