Costruire un oggetto funzione con proprietà a macchina

voti
46

Voglio creare un oggetto funzione, che ha anche alcune proprietà possedute su di esso. Per esempio in JavaScript che vorrei fare:

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

Ora a macchina posso descrivere il tipo di questo come:

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

Tuttavia non posso davvero costruirlo, senza la necessità di un cast. Ad esempio:

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

Come si potrebbe costruire questo senza un cast?

È pubblicato 07/10/2012 alle 07:08
fonte dall'utente
In altre lingue...                            


9 risposte

voti
73

Aggiornamento: Questa risposta è stata la soluzione migliore nelle versioni precedenti di Carattere tipografico, ma ci sono opzioni migliori disponibili in versioni più recenti (vedi altre risposte).

La risposta accettata funziona e potrebbe essere necessario in alcuni casi, ma hanno lo svantaggio di non fornire nessuna sicurezza tipo per costruire l'oggetto. Questa tecnica, almeno gettare un errore di tipo, se si tenta di aggiungere una proprietà non definita.

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

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

// type error
f.notDeclard = 3
Risposto il 05/09/2013 a 16:12
fonte dall'utente

voti
34

Dattiloscritto è progettato per gestire questo caso attraverso dichiarazione fusione :

si può anche avere familiarità con la pratica JavaScript del creazione di una funzione e quindi di estendere la funzione di ulteriormente con l'aggiunta proprietà sulla funzione. Dattiloscritto utilizza dichiarazione di fusione per costruire le definizioni di questo tipo in modo type-safe.

Dichiarazione fusione ci permette di dire che qualcosa è al tempo stesso una funzione e uno spazio dei nomi (modulo interno):

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

Ciò preserva la digitazione e consente di scrivere sia f()e f.someValue. Quando si scrive un .d.tsfile di codice JavaScript esistente, utilizzare declare:

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

Aggiungere proprietà alle funzioni è spesso un modello di confusione o imprevisti a macchina, in modo da cercare di evitarlo, ma può essere necessario quando si utilizza o la conversione più vecchio codice JS. Questo è uno dei pochi tempi sarebbe opportuno mescolare moduli interni (namespace) con esterna.

Risposto il 28/10/2015 a 13:47
fonte dall'utente

voti
27

Ciò è facilmente realizzabile ora (dattiloscritto 2.x) con Object.assign(target, source)

esempio:

entrare descrizione dell'immagine qui

La magia è che Object.assign<T, U>(...)viene digitato per restituire un all'intersezione tipo di T & U.

Applicazione che questo risolve a un'interfaccia nota è anche diretta:

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

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

Errore: foo: numero non assegnabili a pippo: stringa di
errore: numero non assegnabili a string [] (tipo di ritorno)

avvertimento: potrebbe essere necessario Polyfill Object.assign se destinati a un browser più vecchi.

Risposto il 25/01/2017 a 13:43
fonte dall'utente

voti
17

Quindi, se il requisito è quello di costruire in modo semplice e assegnare tale funzione su "f" senza un cast, ecco una possibile soluzione:

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

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

In sostanza, si utilizza una funzione di auto esecuzione letterale per "costruire" un oggetto che abbinerà che firma prima della cessione è fatto. L'unica stranezza è che la dichiarazione interna della funzione deve essere di tipo 'qualsiasi', altrimenti il ​​compilatore grida che si sta assegnando a una proprietà che non esiste sull'oggetto ancora.

EDIT: semplificato il codice un po '.

Risposto il 07/10/2012 a 17:33
fonte dall'utente

voti
1

Non posso dire che è molto semplice, ma è sicuramente possibile:

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

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

se hai curioso questo è da un nocciolo della miniera con la versione dattiloscritto / JavaScript delOptional

Risposto il 07/02/2018 a 23:57
fonte dall'utente

voti
1

Una risposta aggiornamento: dal momento che l'aggiunta di tipi intersezione via &, è possibile "fondere" due tipi desunti al volo.

Ecco un aiutante generale, che legge le proprietà di un oggetto frome li copia su un oggetto onto. Restituisce lo stesso oggetto ontoma con un nuovo tipo che comprende due serie di proprietà, così correttamente descrivere il comportamento di esecuzione:

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

Questo aiutante di basso livello non ancora esegue un tipo di affermazione, ma è type-safe di progettazione. Con questo helper in atto, abbiamo un operatore che possiamo utilizzare per risolvere il problema del PO, con la sicurezza di tipo integrale:

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

Clicca qui per provarlo nel dattiloscritto Playground . Si noti che abbiamo vincolata fooad essere di tipo Foo, quindi il risultato di mergedeve essere un completo Foo. Quindi, se si rinomina bara badallora si ottiene un errore di tipo.

NB C'è ancora un buco tipo qui, tuttavia. Dattiloscritto non fornisce un modo per vincolare un parametro di tipo essere "non una funzione". Così si potrebbe confondersi e passare la vostra funzione come secondo argomento a merge, e che non avrebbe funzionato. Così fino a quando questo può essere dichiarato, dobbiamo prenderlo in fase di esecuzione:

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;
}
Risposto il 10/03/2016 a 19:14
fonte dall'utente

voti
1

Come scorciatoia, è possibile assegnare dinamicamente il valore oggetto utilizzando la funzione di accesso [ 'proprietà']:

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

Questo bypassa il controllo dei tipi. Tuttavia, è abbastanza sicuro perché si deve accedere intenzionalmente la proprietà allo stesso modo:

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

Tuttavia, se si vuole veramente il tipo di controllo per il valore della proprietà, questo non funzionerà.

Risposto il 11/10/2012 a 05:38
fonte dall'utente

voti
0

Vecchia questione, ma per le versioni di dattiloscritto che iniziano con 3.1, si può semplicemente fare l'assegnazione di proprietà, come si farebbe in pianura JS, fino a quando si utilizza una dichiarazione di funzione o la constparola chiave per la variabile:

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

Riferimento e ad esempio on-line .

Risposto il 26/11/2018 a 21:38
fonte dall'utente

voti
0

Questo parte da tipizzazione forte, ma si può fare

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

se si sta cercando di aggirare opprimente tipizzazione forte come mi è stato quando ho trovato questa domanda. Purtroppo questo è un caso dattiloscritto non riesce a perfettamente valido JavaScript quindi bisogna dici dattiloscritto fare marcia indietro.

"Tu JavaScript è perfettamente valida dattiloscritto" FALSE. (Nota: usando 0.95)

Risposto il 21/02/2014 a 16:59
fonte dall'utente

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