Bygg en funksjon objekt med egenskaper i Loggfila

stemmer
51

Jeg ønsker å lage en funksjon objekt, som også har noen egenskaper som holdes på den. For eksempel i Javascript vil jeg gjøre:

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

Nå i Loggfila kan jeg beskrive hvilken type dette som:

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

Men jeg kan faktisk ikke bygge den, uten å kreve en støpt. Som for eksempel:

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

Hvordan vil du bygge dette uten en støpt?

Publisert på 07/10/2012 klokken 06:08
kilden bruker
På andre språk...                            


9 svar

stemmer
80

Oppdatering: Dette svaret var den beste løsningen i tidligere versjoner av maskinskrevet manuskript, men det finnes bedre alternativer tilgjengelig i nyere versjoner (se andre svar).

Den aksepterte svaret fungerer og kan være nødvendig i noen situasjoner, men har ulempen av å gi ingen type sikkerhet for å bygge opp objektet. Denne teknikken vil i det minste kaste en type feil hvis du prøver å legge til en udefinert eiendom.

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

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

// type error
f.notDeclard = 3
Svarte 05/09/2013 kl. 15:12
kilden bruker

stemmer
37

Loggfila er designet for å håndtere denne saken gjennom erklæring sammenslåing :

kan også være kjent med Javascript praksis med å skape en funksjon og deretter utvide funksjonen ytterligere ved å legge til egenskaper til den funksjonen. Loggfila bruker erklæring sammenslåing for å bygge opp definisjoner som dette i et type sikker måte.

Erklæring sammenslåing lar oss si at noe er både en funksjon og en navnerom (intern modul):

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

Dette bevarer skrive og lar oss skrive både f()og f.someValue. Når du skriver en .d.tsfil for eksisterende Javascript-kode, kan du bruke declare:

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

Legge til egenskaper til funksjoner er ofte en forvirrende eller uventet mønster i maskinskrevet manuskript, så prøv å unngå det, men det kan være nødvendig når du bruker eller konvertering av eldre JS kode. Dette er et av de få ganger, vil det være hensiktsmessig å blande interne moduler (navnerom) med utvendig.

Svarte 28/10/2015 kl. 13:47
kilden bruker

stemmer
30

Dette er lett oppnåelig nå (Loggfila 2.x) med Object.assign(target, source)

eksempel:

skriv bildebeskrivelse her

Magien her er at Object.assign<T, U>(...)det blir oppgitt til å returnere en skjæringstype .T & U

Håndheve at dette løser et kjent grensesnitt er også rett fram:

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

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

Feil: foo: nummer ikke kan tilordnes til foo: string
feil: nummer ikke kan tilordnes til String [] (retur-type)

påminnelse: du må kanskje polyfill Object.assign om målretting eldre nettlesere.

Svarte 25/01/2017 kl. 13:43
kilden bruker

stemmer
17

Så hvis kravet er å bare bygge og tildele denne funksjonen til "f" uten en støpt, her er en mulig løsning:

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

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

I hovedsak, bruker den en selv utfører funksjon bokstavelig til "konstruere" et objekt som vil matche den signaturen før oppdraget er ferdig. Den eneste weirdness er at den indre erklæringen av funksjon må være av type 'noen', ellers kompilatoren roper at du tilordner til en eiendom som ikke finnes på objektet ennå.

EDIT: Forenklet koden litt.

Svarte 07/10/2012 kl. 16:33
kilden bruker

stemmer
2

Som en snarvei kan du dynamisk tilordne objektverdien med [ 'eiendom'] tilbehør:

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

Dette forbigår typekontroll. Men det er ganske trygt fordi du må forsettlig tilgang til eiendommen på samme måte:

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

Men hvis du virkelig ønsker den type kontroll for eiendommens verdi, vil dette ikke fungere.

Svarte 11/10/2012 kl. 04:38
kilden bruker

stemmer
1

Jeg kan ikke si at det er veldig grei, men det er definitivt mulig:

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

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

hvis du fikk nysgjerrig dette er fra en kjerne av mine med Loggfila / Javascript-versjonen avOptional

Svarte 07/02/2018 kl. 23:57
kilden bruker

stemmer
1

En oppdatert svar: siden tillegg av krysningstyper via &, er det mulig å "slå sammen" to antatte typer på sparket.

Her er en generell medhjelper som leser egenskapene til et objekt fromog kopierer dem over et objekt onto. Den returnerer samme objekt onto, men med en ny type som omfatter begge settene med egenskaper, så korrekt beskriver runtime oppførsel:

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

Dette lavt nivå helper har fortsatt utføre en type-påstand, men det er type trygt ved design. Med dette hjelper på plass, har vi en operatør som vi kan bruke til å løse OP problem med full typen sikkerhet:

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

Klikk her for å prøve det ut i Loggfila barn . Merk at vi har begrenset footil å være av type Foo, så resultatet av mergemå være en komplett Foo. Så hvis du endrer navn bartil badså du får en type feil.

NB Det er fortsatt en type hull her, men. Maskinskrevet manuskript gir ikke en måte å begrense en type som parameter for å være "ikke er en funksjon". Så du kan bli forvirret og passere din funksjon som andre argument til merge, og det ville ikke fungere. Så inntil dette kan bli erklært, må vi ta det under kjøring:

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;
}
Svarte 10/03/2016 kl. 19:14
kilden bruker

stemmer
0

Gamle spørsmålet, men for versjoner av maskinskrevet manuskript som starter med 3.1, kan du bare gjøre eiendommen oppdrag som du ville gjort i vanlig JS, så lenge du bruker en funksjon erklæringen eller constsøkeord for variabel:

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'"

Referanse og online eksempel .

Svarte 26/11/2018 kl. 21:38
kilden bruker

stemmer
0

Dette avviker fra sterk skrive, men du kan gjøre

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

hvis du prøver å komme seg rundt undertrykkende sterk typing som jeg var da jeg fant dette spørsmålet. Dessverre er dette en sak Loggfila mislykkes på helt gyldig Javascript slik at du må du fortelle Loggfila tilbake av.

"Du Javascript er helt gyldig Loggfila" evalueres til falsk. (Merk: ved hjelp 0.95)

Svarte 21/02/2014 kl. 15:59
kilden bruker

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