Построить функцию объекта со свойствами в машинописном

голоса
46

Я хочу, чтобы создать функциональный объект, который также имеет некоторые свойства, проводимых на нем. Например, в JavaScript Я хотел бы сделать:

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

Теперь в машинописном я могу описать тип это как:

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

Однако я не могу на самом деле построить его, не требуя актеров. Такие как:

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

Как бы вы построить это без гипса?

Задан 07/10/2012 в 07:08
источник пользователем
На других языках...                            


9 ответов

голоса
73

Обновление: Этот ответ был лучшим решением в более ранних версиях машинописи, но есть лучшие варианты, доступные в более новых версиях (см других ответов).

Принятый ответ работает и может потребоваться в некоторых ситуациях, но есть оборотная сторона не предоставляя типобезопасности для создания объекта. Этот метод, по крайней мере, бросить тип ошибки, если вы попытаетесь добавить неопределенное свойство.

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

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

// type error
f.notDeclard = 3
Ответил 05/09/2013 в 16:12
источник пользователем

голоса
34

Машинопись предназначена для обработки этого случая посредством декларации слияния :

Вы также можете быть знакомы с JavaScript практикой создания функции, а затем расширить функции путем добавления свойств на функцию. Машинопись использует декларацию слияние для создания определений, как это так, типобезопасным.

Декларация сращивание позволяет нам сказать, что что-то как функция и пространство имен (внутренний модуль):

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

Это сохраняет типизацию и позволяет нам писать как f()и f.someValue. При написании .d.tsфайла для существующего кода JavaScript, используйте declare:

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

Добавление свойств к функциям часто сбивает с толку или неожиданная закономерность в машинописном, так что постарайтесь, чтобы избежать этого, но это может быть необходимо при использовании или преобразовании старого кода JS. Это один из немногих раз было бы целесообразно, чтобы смешать внутренние модули (пространства имен) с внешним.

Ответил 28/10/2015 в 13:47
источник пользователем

голоса
27

Это легко достижимо сейчас (машинопись 2.x) с Object.assign(target, source)

пример:

введите описание изображения здесь

Магия здесь является то , что Object.assign<T, U>(...)набирается возвращать пересечения типа T & U.

Обеспечение, что это решает известный интерфейс также прямолинейно:

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

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

Ошибка: Foo: номер не присваиваемый обув: строка
ошибка: номер не может быть передан в строку [] (возврат типа)

Предостережение: вам может понадобиться polyfill Object.assign если ориентируетесь старые браузеры.

Ответил 25/01/2017 в 13:43
источник пользователем

голоса
17

Таким образом, если требование просто построить и назначить эту функцию на «F» без броска, вот одно из возможных решений:

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

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

По существу, она использует функцию выполняющегося собственного литерала «построить» объект, который будет соответствовать подписям перед присваиванием выполняется. Единственная странность, что внутренняя декларация функции должна быть типа «любой», в противном случае компилятор кричит, что вы присваиваете свойству, которое не существует на объекте еще.

EDIT: упростили код немного.

Ответил 07/10/2012 в 17:33
источник пользователем

голоса
1

Я не могу сказать, что это очень просто, но это, безусловно, можно:

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

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

если вы получили любопытно это от сути моего с версией Машинопись / JavaScript изOptional

Ответил 07/02/2018 в 23:57
источник пользователем

голоса
1

Обновленный ответ: поскольку добавление типов пересечений через &, можно «объединить» два выведенные типа на лету.

Вот общий помощник , который считывает свойства какого - либо объекта fromи копирует их на объект onto. Она возвращает один и тот же объект , ontoно с новым типом , который включает оба набора свойств, так что правильно описание поведения во время выполнения:

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

Этот помощник низкоуровневого делает еще выполнить тип-утверждение, но это типобезопасная конструкцией. С помощью этого помощника на месте, у нас есть оператор, который мы можем использовать, чтобы решить проблему в OP с полной безопасностью типа:

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

Нажмите здесь , чтобы попробовать его в машинописи Playground . Обратите внимание , что мы сдержали fooбыть типа Foo, поэтому результат mergeдолжен быть полным Foo. Так что если вы переименовать barв badто вы получите сообщение об ошибке типа.

NB Существует еще один тип отверстия здесь, однако. Машинопись не обеспечивает способ , чтобы ограничить параметр типа , чтобы быть «не является функцией». Таким образом , вы можете запутаться и передать вашу функцию в качестве второго аргумента merge, и это не будет работать. Так , пока это не может быть объявлено, мы должны поймать его во время выполнения:

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;
}
Ответил 10/03/2016 в 19:14
источник пользователем

голоса
1

В качестве ярлыка можно динамически присвоить значение объекта, используя [ «свойство»] аксессор:

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

Это обходит проверку типов. Тем не менее, это довольно безопасно, потому что вы должны намеренно получить доступ к собственности таким же образом:

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

Тем не менее, если вы действительно хотите, типа проверки для значения свойства, это не будет работать.

Ответил 11/10/2012 в 05:38
источник пользователем

голоса
0

Старый вопрос, но для версий машинописи , начиная с 3.1, вы можете просто выполнить задание свойств , как если бы в простом JS, до тех пор , пока вы используете объявление функции или constключевое слово переменный:

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

Ссылка и онлайн пример .

Ответил 26/11/2018 в 21:38
источник пользователем

голоса
0

Это отходит от сильной типизации, но вы можете сделать

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

если вы пытаетесь обойти репрессивную строгую типизацию, как я был, когда я нашел этот вопрос. К сожалению, это тот случай, машинопись не будет работать на совершенно правильный JavaScript, так что вы должны сказать вам машинопись отступить.

«Вы JavaScript вполне допустимо Машинопись» оценивается как ложное. (Примечание: с помощью 0,95)

Ответил 21/02/2014 в 16:59
источник пользователем

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