Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 为什么使用“时会丢失第二个泛型类型信息?”;推断;?_Typescript_Generics - Fatal编程技术网

Typescript 为什么使用“时会丢失第二个泛型类型信息?”;推断;?

Typescript 为什么使用“时会丢失第二个泛型类型信息?”;推断;?,typescript,generics,Typescript,Generics,因此,我有一个抽象基类,它接收ValueType的值,并有一个函数,可以将其转换为TransformedType。这可以通过两种泛型来解决: 抽象类基类{ 公共摘要transformValue:(值:ValueType)=>TransformedType; } 现在我有两个类从Base扩展而来,一个用于ValueType字符串,另一个用于数字: 接口字符串转换{ someValue:字符串; } 类DerivedString扩展了基{ 公共转换值=(值:字符串):StringTransform

因此,我有一个抽象基类,它接收
ValueType
的值,并有一个函数,可以将其转换为
TransformedType
。这可以通过两种泛型来解决:

抽象类基类{
公共摘要transformValue:(值:ValueType)=>TransformedType;
}
现在我有两个类从
Base
扩展而来,一个用于
ValueType
字符串,另一个用于数字:

接口字符串转换{
someValue:字符串;
}
类DerivedString扩展了基{
公共转换值=(值:字符串):StringTransformed=>{
返回{someValue:value+“someValue”};
};
}
接口号已转换{
valuePlusPlus:数字;
}
类DerivedNumber扩展了基{
public transformValue=(值:number):NumberTransformed=>{
返回{valuePlusPlus:value+1};
};
}
之后,我创建了一个类型
字段
,它需要一个通用类型
T
,来自类型
对象

类型字段={
[A in keyof T]:任何扩展基?基:从不
};
用法示例:

typemaintype={x:string;y:number};
常量字段:字段={
x:新衍生字符串(“测试”),
y:新衍生编号(2)
};
const transformed=fields.x.transformValue(“任意字符串”);
这里的问题是关于
TransformedType
的类型信息丢失,因为它是
未知的

与该类型不同,关于
ValueType
的信息保持正确

问题:

  • 如何在不增加任何限制的情况下正确地采用泛型类型?
    在这里使用
    推断
    正确吗?
    为什么类型信息丢失

  • 我是否能够从
    Base
    的泛型参数中删除
    TransformedType
    ,并仅在
    transformValue
    函数中使用它?(
    ValueType
    需要在
    Base
    中使用其他函数等,但
    TransformedType
    仅用于
    transformValue
    函数)

  • 编辑:

    根据注释的要求,我创建了一个带有用例示例的示例。基本上有一些
    any
    cast我无法回避,这是我的实际问题


    一般来说,我想在react中编写一个库来处理表单状态,所以它确实需要一种非常通用的方法。请记住,这个例子实际上非常简单,实际上要复杂得多

    好的,对于第一个问题2:是的,您可以删除第二个类型参数,并将其默认为
    unknown
    ,如果您这样定义它:

    // default second parameter
    abstract class Base<T, U = unknown> {
      public abstract transformValue: (value: T) => U;
    }
    
    
    interface StringTransformed {
      someValue: string;
    }
    
    class DerivedString extends Base<string> {
      public transformValue = (value: string): StringTransformed => {
        return { someValue: value + "someValue" };
      };
    }
    
    interface NumberTransformed {
      valuePlusPlus: number;
    }
    
    class DerivedNumber extends Base<number> {
      public transformValue = (value: number): NumberTransformed => {
        return { valuePlusPlus: value + 1 };
      };
    }
    
    const asFields = <F extends Fields>(fields: F) => fields;
    
    几乎一样。请注意,我给出了一个默认类型参数
    any
    ,因此
    Fields
    本身意味着
    Fields助手函数手动指定
    T
    ,并让它推断出其余部分:

    const asValueFields = <T>() => <F extends Fields<T>>(fields: F) => fields;
    

    最后,让我们实现
    transformFields()
    。对于给定的
    字段
    类型,描述转换后的输出类型很有用。以下是我对它的定义:

    type TransformedType<F extends Fields> = {
      [K in keyof F]: F[K] extends Base<any, infer T> ? T : never
    };
    
    该实现与代码沙箱中的实现相同,其中一些是经过仔细放置的,以使编译器相信您所做的是正确的。基本上,我们断言
    Object.keys(fields)
    将返回
    T
    的键数组,
    fields[key]
    是一个
    Base
    ,它接受
    T[K]
    并输出
    TransformedType[K]
    ,而
    reduce()
    累加器是一个
    TransformedType

    唷!让我们使用它:

    const transformedMainType = transformFields(fields, { x: "", y: 1 });
    transformedMainType.x.someValue; // okay
    transformedMainType.y.valuePlusPlus; // okay
    
    看起来不错。请注意,您不必从以前开始使用helper函数“prep”
    字段。如果您有一个正确类型的对象文字,它也会正常工作:

    const alsoWorks = transformFields(
      { x: new DerivedString(), y: new DerivedNumber() },
      { x: "", y: 1 }
    );
    alsoWorks.x.someValue; // okay
    alsoWorks.y.valuePlusPlus; // okay
    
    好吧,那太多了。希望这能给你一些指导。祝你好运


    类型信息看起来不像一开始就不存在的那样“丢失”<代码>任何扩展…推断…
    几乎不会对您有多大帮助,因为您正在分析
    任何
    。为什么不干脆去掉
    字段的注释
    并让
    常量字段={x:new-DerivedString(),y:new-DerivedNumber()}
    ?如果这不起作用,你能告诉我在哪里吗?祝你好运@我编辑了我的问题并添加了一个代码沙盒。希望这将有助于更好地理解我的问题。遗憾的是,我不能仅仅使用
    字段的注释,因为我希望一个通用的
    字段
    作为函数的参数(请参见
    transformFields.ts
    中的codesandbox)哇,这非常有帮助和详细!使用它已经在我的代码和作品像一个魅力,感谢很多打字大师!
    const asValueFields = <T>() => <F extends Fields<T>>(fields: F) => fields;
    
    const fieldsAlso = asValueFields<MainType>()({
      x: new DerivedString(),
      y: new DerivedNumber()
    });
    
    const badFieldsAlso = asValueFields<MainType>()({
      x: new DerivedNumber(), // error! string is not number
      y: new DerivedString()  // error! number is not string
    });
    
    fieldsAlso.x.transformValue("anystring").someValue; // okay
    
    type TransformedType<F extends Fields> = {
      [K in keyof F]: F[K] extends Base<any, infer T> ? T : never
    };
    
    const transformFields = <T extends object, F extends Fields<T>>(
      fields: F,
      values: T
    ): TransformedType<F> => {
      return (Object.keys(fields) as Array<keyof T>).reduce(
        <K extends keyof T>(transformedObj: TransformedType<F>, key: K) => ({
          ...transformedObj,
          [key]: (fields[key] as Base<T[K], TransformedType<F>[K]>).transformValue(
            values[key]
          )
        }),
        {} as TransformedType<F>
      );
    };
    
    const transformedMainType = transformFields(fields, { x: "", y: 1 });
    transformedMainType.x.someValue; // okay
    transformedMainType.y.valuePlusPlus; // okay
    
    const alsoWorks = transformFields(
      { x: new DerivedString(), y: new DerivedNumber() },
      { x: "", y: 1 }
    );
    alsoWorks.x.someValue; // okay
    alsoWorks.y.valuePlusPlus; // okay