如何将已知的接口属性与自定义索引签名相结合?

pap*_*boy 4 typescript index-signature

你如何输入一个可以同时拥有几个声明的可选属性的对象,例如:

{ 
    hello?: string, 
    moo?: boolean 
}
Run Code Online (Sandbox Code Playgroud)

以及自定义属性(必须是函数),例如:

    [custom: string]: (v?: any) => boolean
Run Code Online (Sandbox Code Playgroud)

这是我希望看到的,例如:

const myBasic: Example = {moo: false}
// -> ? Valid! Using known keys

const myValid: Example = {hello: 'world', customYo: () => true}
// -> ? Valid! "customYo" is a function returning a bool. Good job!

const myInvalid: Example = {hello: 'world', customYo: 'yo!'}
// -> ?? Invalid! "customYo" must be a function returning a boolean
Run Code Online (Sandbox Code Playgroud)

尝试将索引签名添加到具有已知键的接口(即hello?: string, moo?: boolean)需要所有键都是索引签名类型的子集(在这种情况下,返回 a 的函数boolean)。这显然失败了。

Nic*_*ght 6

业主接受的问题(直到现在)是不正确的。

您可以这样做:

您需要使索引签名成为接口中可以包含的所有类型的联合类型:

interface IExample {
    hello?: string;
    moo?: boolean;
    [custom: string]: string | boolean | YourFunctionType;
}

interface YourFunctionType {
    (v?: any): boolean;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我已将您的函数类型提取到单独的界面中以提高可读性。

含义:

这意味着 TS 很好地支持显式定义的属性:

const test: IExample = <IExample>{};
test.hello.slice(2); // using a string method on a string --> OK
const isHello = test.hello === true; // ERROR (as expected): === cannot be applied to types string and boolean
const isMoo2 = test.moo === true; // OK
Run Code Online (Sandbox Code Playgroud)

但是,现在需要使用类型保护检查索引签名中的所有属性,这会增加一点运行时开销:

test.callSomething(); // ERROR: type 'string | boolean | YourFunctionType' has no compatible call signatures
if (typeof test.callSomething === 'function') { // alternatively you can use a user defined type guard, like Lodash's _.isFunction() which looks a little bit nicer
    test.callSomething(); // OK
}
Run Code Online (Sandbox Code Playgroud)

另一方面:运行时开销是必要的,因为它可能是这样test访问的:

const propertyName: string = 'moo';
test[propertyName](); // ERROR: resolves to a boolean at runtime, not a function ...

// ... so to be sure that an arbitrary propertyName can really be called we need to check:
const propertyName2: string = 'arbitraryPropertyName';
const maybeFunction = test[propertyName2];
if (typeof maybeFunction === 'function') {
    maybeFunction(); // OK
}
Run Code Online (Sandbox Code Playgroud)


ale*_*ill 5

这是不可能的,设计https://basarat.gitbooks.io/typescript/docs/types/index-signatures.html

一旦您拥有字符串索引签名,所有显式成员也必须符合该索引签名。这是为了提供安全性,以便任何字符串访问都给出相同的结果。

绕过它的唯一方法是利用每个接口可以有 2 个单独的索引签名,一个用于stringnumber

在你例子hellomoo使字符串指数无法使用,但你可以劫持为自定义方法的数量指标

interface IExample {
  hello?: string
  moo?: boolean
  [custom: number]: (v?: any) => boolean
}

const myBasic: IExample = {moo: false}
// -> ? Valid! Using known keys

const myValid: IExample = {hello: 'world', 2: () => true}
// -> ? Valid! "customYo" is a function returning a bool. Good job!

const myInvalid: IExample = {hello: 'world', 2: 'yo!'}
// -> ?? Invalid! "customYo" must be a function returning a boolean
Run Code Online (Sandbox Code Playgroud)

这有效但几乎不是可接受的接口,因为会导致不直观的函数,您必须通过数组表示法调用它们

myValid.7() // Cannot invoke an expression whose type lacks a call signature. Type 'Number' has no compatible call signatures.
myValid[2]() // works (but ewwwww what is this!!!)
// could alias to more readable locals later but still ewwwwww!!! 
const myCustomFunc = myValid[2]
myCustomFunc() // true
Run Code Online (Sandbox Code Playgroud)

这也有一个警告,即从数字索引器返回的类型必须是从字符串索引器返回的类型的子类型。这是因为当用数字索引时,javascript 会在索引到对象之前将数字转换为字符串

在这种情况下,您没有显式的字符串索引器,因此字符串索引类型是any数字索引器类型可以符合的默认值

重要这只是为了科学,我不建议将其作为现实生活的方法!