Я хочу создать функциональный объект, который также имеет некоторые свойства. Например, в JavaScript я бы сделал:
var f = function() { }
f.someValue = 3;
Теперь в TypeScript я могу описать этот тип как:
var f: { (): any; someValue: number; };
Однако я не могу построить его, не требуя гипса. Такие как:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
Как бы вы построили это без гипса?
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
.Ответы:
Итак, если требуется просто построить и присвоить эту функцию «f» без приведения, вот возможное решение:
var f: { (): any; someValue: number; }; f = (() => { var _f : any = function () { }; _f.someValue = 3; return _f; })();
По сути, он использует самоисполняющийся функциональный литерал для «конструирования» объекта, который будет соответствовать этой сигнатуре до того, как будет выполнено назначение. Единственная странность заключается в том, что внутреннее объявление функции должно иметь тип 'any', иначе компилятор будет кричать, что вы назначаете свойство, которое еще не существует в объекте.
РЕДАКТИРОВАТЬ: немного упростил код.
источник
Обновление: этот ответ был лучшим решением в более ранних версиях TypeScript, но в более новых версиях доступны лучшие варианты (см. Другие ответы).
Принятый ответ работает и может потребоваться в некоторых ситуациях, но имеет недостаток в том, что он не обеспечивает типобезопасности для создания объекта. Этот метод, по крайней мере, вызовет ошибку типа, если вы попытаетесь добавить неопределенное свойство.
interface F { (): any; someValue: number; } var f = <F>function () { } f.someValue = 3 // type error f.notDeclard = 3
источник
var f
строка не вызывает ошибку, ведь на тот моментsomeValue
свойства нет .var f:F = function(){}
что не удастся в приведенном выше примере. Этот ответ не подходит для более сложных ситуаций, поскольку вы теряете проверку типов на этапе назначения.<F><any>function() { ... }
var f = (function () {}) as any as F;
Теперь это легко достижимо (машинописный текст 2.x) с помощью
Object.assign(target, source)
пример:
Магия здесь в том, что
Object.assign<T, U>(t: T, u: 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 } );
какие ошибки из-за несовместимой печати:
предостережение: вам может понадобиться полифилить Object.assign, если вы ориентируетесь на старые браузеры.
источник
TypeScript предназначен для обработки этого случая путем слияния деклараций :
Объединение деклараций позволяет нам сказать, что что-то является одновременно функцией и пространством имен (внутренним модулем):
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; }
Добавление свойств к функциям часто является запутанным или неожиданным шаблоном в TypeScript, поэтому старайтесь избегать этого, но это может быть необходимо при использовании или преобразовании старого кода JS. Это один из немногих случаев, когда было бы целесообразно смешивать внутренние модули (пространства имен) с внешними.
источник
function hello() { .. }
namespace hello { export const value = 5; }
export default hello;
IMO, это намного чище, чем Object.assign или аналогичные хаки времени выполнения. Ни времени выполнения, ни утверждений типа, ничего.Не могу сказать, что это очень просто, но это определенно возможно:
interface Optional { <T>(value?: T): OptionalMonad<T>; empty(): OptionalMonad<any>; } const Optional = (<T>(value?: T) => OptionalCreator(value)) as Optional; Optional.empty = () => OptionalCreator();
Если вам интересно, это из моей сути с версией TypeScript / JavaScript
Optional
источник
В качестве ярлыка вы можете динамически назначать значение объекта с помощью метода доступа ['property']:
var f = function() { } f['someValue'] = 3;
Это обходит проверку типа. Однако это довольно безопасно, потому что вы должны намеренно получить доступ к свойству таким же образом:
var val = f.someValue; // This won't work var val = f['someValue']; // Yeah, I meant to do that
Однако, если вы действительно хотите проверить тип значения свойства, это не сработает.
источник
Обновленный ответ: после добавления типов пересечений через
&
, можно «на лету» «слить» два предполагаемых типа.Вот общий помощник, который считывает свойства некоторого объекта
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}`) } } );
Щелкните здесь, чтобы опробовать его на площадке для игр TypeScript . Обратите внимание, что мы ограничились
foo
типомFoo
, поэтому результатmerge
должен быть полнымFoo
. Поэтому, если вы переименуетеbar
в,bad
вы получите ошибку типа.NB Однако здесь есть еще одно типовое отверстие. TypeScript не позволяет ограничить параметр типа «не функцией». Так что вы можете запутаться и передать свою функцию в качестве второго аргумента
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; }
источник
Старый вопрос, но для версий TypeScript, начиная с 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'"
Ссылка и онлайн-пример .
источник
Это отличается от строгой типизации, но вы можете
var f: any = function() { } f.someValue = 3;
если вы пытаетесь обойти тяжелую строгую типизацию, как я, когда нашел этот вопрос. К сожалению, это тот случай, когда TypeScript не работает на совершенно корректном JavaScript, поэтому вам нужно сказать TypeScript, чтобы он отступил.
«Ваш JavaScript - совершенно правильный TypeScript» оценивается как false. (Примечание: используется 0,95)
источник