如何在打字稿中定义同时具有对象和功能的类型?

时间:2017-08-24 12:24:24

标签: javascript typescript types prototype

在阅读问题How is jQuery's $ a function and an object?的{​​{3}}之后,它让我思考。你会如何在打字稿中定义这种类型?

在常规JS中,这是完全有效的:

var f = function() { alert('yo'); }
f.foo = "bar";

alert(f.foo); // alerts "bar"
f();          // alerts "yo"

但是在打字稿中,f.foo会抛出错误Property 'foo' does not exist on type '() => void'

虽然您可以使用this answer获得类似的结果:

var f = function() { alert('yo'); }
f['foo'] = "bar";

alert(f['foo']); // alerts "bar"
f();          // alerts "yo"

这将完全绕过类型系统,以及打字稿的类型安全。

有没有办法在不违反打字稿类型安全的情况下实现此类功能?

2 个答案:

答案 0 :(得分:3)

您可以通过在接口上指定调用签名来定义此类型:

interface MyFoo {
    (): void;
    foo: string;
}

您可以通过将函数转换为any来初始化对象,然后再将其赋值给变量:

let f: MyFoo = function() { alert('yo'); } as any;
f.foo = 'bar';
x();

Playground link

另外,JQueryStatic中的$类型(interface JQueryStatic<TElement extends Node = HTMLElement> { ... Deferred: JQuery.DeferredStatic; ... (html: JQuery.htmlString, ownerDocument_attributes: Document | JQuery.PlainObject): JQuery<TElement>; ... (selector: JQuery.Selector, context: Element | Document | JQuery | undefined): JQuery<TElement>; 变量的类型)的定义类似:

<!DOCTYPE html>
<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <title>Get Directory</title>
    <!-- Update your jQuery version??? -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>


      <!--
  https://cdnjs.com/libraries/crypto-js
  -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>


  <!--[if lt IE 9]>
    <script src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->

    <script> // type="text/javascript" is unnecessary in html5

    // Short version of doing `$(document).ready(function(){`
    // and safer naming conflicts with $
    jQuery(function($) { 

        $('#file-input').on('change', function() {

            // You can't use the same reader for all the files
            // var reader = new FileReader

            $.each(this.files, function(i, file) {

                // Uses different reader for all files
                var reader = new FileReader

                reader.onload = function() {
                    // reader.result refer to dataUrl
                    // theFile is the blob... CryptoJS wants a string...

                   var encrypted = CryptoJS.AES.encrypt(reader.result, '12334');




                    var ecr = encrypted.toString();


                    var blob = new Blob([ecr], {
                    "type": "text/plain"

                    });

                    var link = document.createElement("a");
                    link.download = "Encrypteddocument";
                    link.href = URL.createObjectURL(blob);


                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);

                    //alert(encrypted);



                }

                reader.readAsDataURL(file)
                $('#thelist').append('FILES: ' + file.name + '<br>')
            })
        })
    });

    </script>
</head>
<body>
    <input type="file" id="file-input">
    <div id="thelist"></div>


    <input type="button" id="button" value="Save" />
</body>
</html>

(不能将链接内联,所以在这里)

但是,这并不是真的有用,因为您的问题也是如何使用此类型初始化变量。 JQuery库没有这个问题,因为它没有写在Typescript中。

答案 1 :(得分:1)

您可以使用&来定义具有多种类型的变量。它被称为“交叉类型”。 official docs中的更多信息。

以下是在您的示例中使用它的方法:

let f: Function & {foo?: String} = function() { alert('yo'); }
f.foo = "bar";

alert(f.foo); // alerts "bar"
f();          // alerts "yo"

Playground Link

此处,f的类型为Function & {foo?: String},因为它既是Function,也是{foo?: String}。此处需要?,因为您只将其初始化为Function,然后才将值分配给foo

您可以在Playground中看到代码编译,甚至提供您开始使用的相同JavaScript代码,验证这是正确的语法。