建立在打字稿性质的函数对象(Build a function object with propert

2019-06-18 11:28发布

我想创建一个函数对象,其中也有它持有一些属性。 例如在JavaScript中我会做:

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

现在,在我的打字原稿可以将此描述为的类型:

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

但是我不能真正建立它,而无需转换。 如:

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

你将如何建立这个没有投?

Answer 1:

因此,如果要求是简单地构建和功能分配到“f”不进行强制转换,这里是一个可能的解决方案:

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

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

本质上,它采用的是自执行函数文本“构造”的对象分配完成之前,将匹配的签名。 唯一古怪的是,该功能的内部声明必须是类型“任意”的,否则编译器的呼喊,你分配给它没有在对象上还存在一个属性。

编辑:简化的代码位。



Answer 2:

更新:这个答案是在早期版本的打字稿的最佳解决方案,但也有新的版本(见其他答案)提供更好的选择。

接受答案的工作,并可能在某些情况下是必需的,但必须提供建立对象没有类型安全的缺点。 这种技术将至少抛出一个类型的错误,如果你尝试添加一个未定义的属性。

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

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

// type error
f.notDeclard = 3


Answer 3:

现在这是很容易实现(打字稿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 }
); 

这是由于不兼容的输入错误:

错误:FOO:数不分配给foo:字符串
错误:号码不能分配给串[](返回类型)

警告:您可能需要填充工具 ,如果针对旧版本浏览器Object.assign。



Answer 4:

打字稿是专门用来处理通过这个案例申报合并

你也可以是熟悉创建一个功能,然后通过添加属性到功能进一步延伸的功能的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代码时,它是必要的。 这是唯一的时候,它是适当的内部模块(命名空间)与外部混合之一。



Answer 5:

作为快捷方式,则可以使用[“属性”]存取动态分配对象值:

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

这种绕过类型检查。 但是,因为你要有意访问属性相同的方式,它是非常安全:

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

但是,如果你真的想要的类型检查的属性值,这是不行的。



Answer 6:

更新的答案:因为通过添加交集类型& ,就可以“合并”两种推断类型的飞行。

下面是一些读对象的属性一般帮手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}`)
        }
    }
);

点击这里尝试一下了打字稿游乐场 。 请注意,我们已经限制foo为类型Foo ,所以结果merge已经是一个完整的Foo 。 所以,如果你重新命名barbad ,那么你得到一个类型的错误。

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


Answer 7:

我不能说这是很简单,但它绝对有可能:

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

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

如果你有自信,这是从我的使用的打字稿/ JavaScript版本要旨Optional



Answer 8:

这偏离了强类型,但你可以做

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

如果你想避开压迫感强类型像我是当我发现这个问题。 可悲的是,这是对案件的打字稿未能完全有效的JavaScript,所以你必须告诉打字稿打退堂鼓了。

“你JavaScript是非常有效的打字稿”评估为假。 (注:使用0.95)



Answer 9:

老问题,但首先是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'"

参考和在线例子 。



文章来源: Build a function object with properties in TypeScript