Typescript:函数返回类型取决于输入函数的返回类型

时间:2018-06-29 11:08:10

标签: typescript generics type-inference

我有一个函数,该函数需要参数中的另一个函数。 我想返回由传入参数的函数的返回类型配置的通用接口。

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.appname.dev, PID: 25576
              java.lang.RuntimeException: Unable to start activity ComponentInfo{com.appname.dev/com.facebook.accountkit.ui.AccountKitActivity}: android.view.InflateException: Binary XML file line #26: Binary XML file line #26: Error inflating class TextView
                  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
                  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2906)
                  at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4778)
                  at android.app.ActivityThread.-wrap18(Unknown Source:0)
                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1611)
                  at android.os.Handler.dispatchMessage(Handler.java:105)
                  at android.os.Looper.loop(Looper.java:172)
                  at android.app.ActivityThread.main(ActivityThread.java:6637)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
               Caused by: android.view.InflateException: Binary XML file line #26: Binary XML file line #26: Error inflating class TextView
               Caused by: android.view.InflateException: Binary XML file line #26: Error inflating class TextView
               Caused by: java.lang.UnsupportedOperationException: Can't convert value at index 37 to dimension: type=0x1
                  at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:730)
                  at android.view.View.<init>(View.java:4998)
                  at android.widget.TextView.<init>(TextView.java:824)
                  at android.widget.TextView.<init>(TextView.java:818)
                  at android.support.v7.widget.ab.<init>(AppCompatTextView.java:76)
                  at android.support.v7.widget.ab.<init>(AppCompatTextView.java:72)
                  at android.support.v7.app.AppCompatViewInflater.a(AppCompatViewInflater.java:176)
                  at android.support.v7.app.AppCompatViewInflater.a(AppCompatViewInflater.java:101)
                  at android.support.v7.app.j.b(AppCompatDelegateImplV9.java:1035)
                  at android.support.v7.app.j.onCreateView(AppCompatDelegateImplV9.java:1092)
                  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:772)
                  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
                  at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
                  at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
                  at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
                  at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
                  at com.facebook.accountkit.ui.be$a.a(TitleFragmentFactory.java:87)
                  at com.facebook.accountkit.ui.ai.onCreateView(LoginFragment.java:43)
                  at com.facebook.accountkit.ui.be$a.onCreateView(TitleFragmentFactory.java:50)
                  at android.app.Fragment.performCreateView(Fragment.java:2611)
                  at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1276)
                  at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1549)
                  at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1611)
                  at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3039)
                  at android.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:2991)
                  at android.app.FragmentController.dispatchActivityCreated(FragmentController.java:178)
                  at android.app.Activity.performCreateCommon(Activity.java:6969)
                  at android.app.Activity.performCreate(Activity.java:6977)
                  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
                  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2784)
                  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2906)
                  at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4778)
                  at android.app.ActivityThread.-wrap18(Unknown Source:0)
                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1611)
                  at android.os.Handler.dispatchMessage(Handler.java:105)
                  at android.os.Looper.loop(Looper.java:172)
                  at android.app.ActivityThread.main(ActivityThread.java:6637)
E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

然后,我想使getter函数成为可选函数,并为此使用默认值。当时发生了问题。

function doSomething <T>(values: Whatever[], getter: (whatever: Whatever) => T): T[] {
  return values.map(value => getter(value));
}

所以现在我得到错误提示:

  

错误:(18,47)TS2322:类型'(val:Whatever)=>什么都不能分配给类型'(whatever:Whatever)=> T'。     无法将“无论”类型分配给“ T”类型。

您知道为什么我会收到该错误吗?

先谢谢您

(以下示例不是我的真实代码,但这对于描述我的问题更加清楚)

我正在使用打字稿2.7.2

2 个答案:

答案 0 :(得分:4)

问题在于默认的getter函数的返回类型为Whatever,但是doSomething声明要求getter必须返回TT是泛型类型参数,可以是任何类型,不能保证WhateverT兼容。 TypeScript编译器看不到提供默认值时不需要T,并且返回的doSomething类型为Whatever[]。但是您可以使用doSomething的这些重载声明来表达它:

function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T): T[];
function doSomething(values: Whatever[]): Whatever[];
// the implementation must be compatible with both variants
function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T | Whatever = val => val ): (T | Whatever)[] {
  return values.map(value => getter(value));
}

更新以解决明确的问题

  

我想避免返回“ Whatever | T”,因为每次我都会   调用此函数,我将必须检查响应类型(无论是   T)。

调用此函数时,仅考虑两个重载签名,当在doSomething()的调用站点执行重载解析时,TypeScript不使用实现签名。实际上,实现返回类型可以简单地声明为any,因为它是在重载documentation examples中完成的-它仅用于对实现进行类型检查,并且实现通常很明显,因此更严格的类型可以在那里没有提供太多好处。

  

我想编写代码以获取getter函数的返回类型并用作T。

如果在调用doSomething时省略了通用参数,则编译器将从T返回类型中推断getter。我认为以下示例可以满足您的要求:

interface Whatever { w: string };

function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T): T[];
function doSomething(values: Whatever[]): Whatever[];
function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T | Whatever = val => val ): (T | Whatever)[] {
  return values.map(value => getter(value));
}

function example() {
    const getter1 = (whatever: Whatever) => whatever.w; // returns string
    const getter2 = (whatever: Whatever) => whatever.w.length; // returns number

    const values: Whatever[] = [];

    const r0 = doSomething(values); // const r1: Whatever[]
    const r1 = doSomething(values, getter1); // const r1: string[]
    const r2 = doSomething(values, getter2); // const r2: number[]
}

答案 1 :(得分:0)

如您所希望的那样,在没有给出getter的情况下采取默认行为,我认为完全可以尝试使用默认的getter进行强制转换:val => val as T

function doSomething <T>(values: Whatever[], getter: (whatever: Whatever) => T = val => val as T): T[] {
    return values.map(getter);
}

并且Typescript仅是“编译时”类型安全的语法糖,这意味着:“尝试按原样获取值,而无需任何映射”。因此,如果您仔细考虑一下,如果没有给出getter,则意味着它实际上不会更改数组的值。根据使用情况,您也可以这样做

function doSomething <T>(values: Whatever[], getter?: (whatever: Whatever) => T): T[] {
    if (getter === undefined) { return values as T[]; }

    return values.map(value => getter(value));
}

并跳过map(),该操作除了复制数组外什么都不做,如果那是您想要的那种情况: return [...values] as T[]代替返回副本。