Reactjs 有没有办法从作为泛型传递的接口推断特定值的类型?

Reactjs 有没有办法从作为泛型传递的接口推断特定值的类型?,reactjs,typescript,Reactjs,Typescript,我完全可能没有正确地理解这一点,因为我是TS新手,但我想知道我是否有某种泛型类型,通过它我将传递一个类似于{[key:string]:string | boolean}的接口,是否有可能知道使用该泛型的函数中任何特定值的类型 例如,我有一个如下界面: export interface Address { addressId: string; firstName: string; lastName: string; addressType: string;

我完全可能没有正确地理解这一点,因为我是TS新手,但我想知道我是否有某种泛型类型,通过它我将传递一个类似于
{[key:string]:string | boolean}
的接口,是否有可能知道使用该泛型的函数中任何特定值的类型

例如,我有一个如下界面:

export interface Address {
    addressId: string;
    firstName: string;
    lastName: string;
    addressType: string;
    addressLine1: string;
    addressLine2: string;
    city: string;
    state: string;
    country: string;
    zipCode: string;
    email1: string;
    phone1: string;
    primary: boolean;
    nickName?: string;
}
我试着这样做:

function useForm<T>(initialValues: T) {
  const [form, setForm] = useState<T>(initialValues);

  const field = (key: keyof T) => {

    const setValue = (newValue: T[keyof T]) => {
      const newState = {
        ...form,
        [key]: newValue
      }
      setForm(newState);
    }
    const value = form[key];
    return {
      value,
      setValue
    };
  }

  return {
    form,
    field,
  };
}
interface Address {
    addressId: string;
    firstName: string;
    lastName: string;
    addressType: string;
    addressLine1: string;
    addressLine2: string;
    city: string;
    state: string;
    country: string;
    zipCode: string;
    email1: string;
    phone1: string;
    primary: boolean;
    nickName?: string;
}

...
const { field, onSubmit } = useForm<Address>(someInitialAddress);
...
<TextField
  field={field('firstName')}
  label="First Name"
  placeholder="First Name"
/>

This returns the field prop as:
field: {
  setValue(value: string): void;
  value: string;
};

...

<CheckboxField
  field={field('primary')}
  label="Use as my primary address for Billing and Shipping"
/>
This returns the field prop as:
field: {
  setValue(value: boolean): void;
  value: boolean;
};

在我看来

const {form, field} = useForm<Address>(someInitialAddress);
...
<TextField field={form("FirstName")} />
...
const{form,field}=useForm(someInitialAddress);
...
...
如果您尝试加载接口上不存在的字段,它已经抛出了一个类型错误,但我想保护您,以确保您没有将基于布尔值或其他内容加载到该字段中,并且我当前收到了此类型错误:
类型“{value:string | boolean | undefined;setValue:(newValue:string | boolean | undefined)=>void;}”不能分配给类型“{setValue:(value:string)=>void;value:string;}”。


非常感谢您的帮助,谢谢

因此,在TypeScript中可能有一些更为开箱即用的东西,但这个问题似乎已经用现有的实用程序类型解决了

根据本文:

你可以为这个类定义一个类型,比如
子类型
,它将用类型T的一个子集构造一个新类型,它属于类型a。我很确定这就是你想要的

子类型定义-这将为不属于类型A的属性分配类型
never
,因此不允许使用这些属性,并在intellisense中显示编译错误等

导出类型子类型=拾取;
然后,您可以像这样将其用于
setValue
函数:

const setValue=(newValue:T[keyof SubType])=>{
常数newState={
…形式,
[键]:newValue
}
setForm(新闻状态);
}
给定以下界面
Test
-您应该看到以下类型检查行为:

导出接口测试{
stringKey:string;
布尔键:布尔;
numberKey:数字;
}
此.setValue(true);//Err true不可分配给类型字符串
此.setValue(5);//错误5不可分配给类型字符串
此.setValue('string');//无误
唯一的问题是,您仍然可以传递NULL和Undefined而不会出错。如果你阅读链接文章中的评论,我认为上面的解决方案有一些调整可以解决这个问题——如果我有时间,我会编辑这个


希望能有所帮助-谢谢你的挑战

这似乎满足了我的需要:

function useForm<T>(initialValues: T) {
  const [form, setForm] = useState<T>(initialValues);

  function field<K extends keyof T>(key: K) {
    function setValue<PropType extends T[K]>(newValue: PropType) {
      const newState = {
        ...form,
        [key]: newValue,
      };
      setForm(newState);
    };
    const value = form[key];
    return {
      value,
      setValue,
    };
  };

  return {
    form,
    field,
  };
}
函数useForm(初始值:T){
const[form,setForm]=useState(初始值);
功能字段(键:K){
函数setValue(新值:PropType){
常数newState={
…形式,
[key]:newValue,
};
setForm(新闻状态);
};
常量值=形式[键];
返回{
价值
设置值,
};
};
返回{
形式,
领域
};
}
这样使用时:

function useForm<T>(initialValues: T) {
  const [form, setForm] = useState<T>(initialValues);

  const field = (key: keyof T) => {

    const setValue = (newValue: T[keyof T]) => {
      const newState = {
        ...form,
        [key]: newValue
      }
      setForm(newState);
    }
    const value = form[key];
    return {
      value,
      setValue
    };
  }

  return {
    form,
    field,
  };
}
interface Address {
    addressId: string;
    firstName: string;
    lastName: string;
    addressType: string;
    addressLine1: string;
    addressLine2: string;
    city: string;
    state: string;
    country: string;
    zipCode: string;
    email1: string;
    phone1: string;
    primary: boolean;
    nickName?: string;
}

...
const { field, onSubmit } = useForm<Address>(someInitialAddress);
...
<TextField
  field={field('firstName')}
  label="First Name"
  placeholder="First Name"
/>

This returns the field prop as:
field: {
  setValue(value: string): void;
  value: string;
};

...

<CheckboxField
  field={field('primary')}
  label="Use as my primary address for Billing and Shipping"
/>
This returns the field prop as:
field: {
  setValue(value: boolean): void;
  value: boolean;
};

接口地址{
addressId:字符串;
名字:字符串;
lastName:string;
addressType:字符串;
addressLine1:字符串;
addressLine2:字符串;
城市:字符串;
状态:字符串;
国家:字符串;
zipCode:字符串;
email1:string;
phone1:字符串;
初级:布尔型;
昵称?:字符串;
}
...
const{field,onSubmit}=useForm(someInitialAddress);
...
这会将字段属性返回为:
字段:{
setValue(值:字符串):void;
值:字符串;
};
...
这会将字段属性返回为:
字段:{
setValue(值:布尔):无效;
值:布尔值;
};

我可能会深入挖掘并跟进,但只要看一看,我就会指出——当你开始谈论“值”的类型时,你谈论的是运行时,javascript类型的信息将从输出中完全剥离出来。在运行时,如果您在保护函数中的值类型,我很确定您将在JS(即typeof)@MrRobboto中检查标准运行时类型,我想我明白了,但是我似乎不清楚为什么
表单
函数可以知道在接口中创建类型的并集,以及知道接口中的所有键,但是似乎没有办法说,给定这个接口和键
foo
,确保返回的函数的类型检查与该值的特定类型匹配。啊,我看到现在你有了一个文本,所以不用担心运行时的东西。我刚刚被“值”这个词甩了,但对于文字来说完全有意义。我仍在努力解决你的问题,没有完全明白。不过,我认为
可能会帮助你,在黑暗中拍摄一点。我已经用过几次了,在做一般性的事情时,我试图说一个参数是K类型的,这是一个keyof T,还有一个字符串。但是我仍然没有对它有一个很好的理解-TypeScript文档,因为它在IMO中没有说足够的内容。哦,同样相关的是,你使用的是什么版本的TS?他们在2.9中改变了T键,我想也许我仍然没有解释我想解决的问题。除非我误解了你的解决方案。以我在OP中的例子为例,我主要希望初始化时像
const{field}=useForm(initAddress)
那样,你可以让它像
form('firstName')=>{setValue:(value:string)=>void,value:string}
但是
form('primary')=>{setValue:(value:boolean)=>void,value:boolean}
在你的例子中,
const setValue=(newValue:T[keyof SubType])=>{
在我的例子中,我并不总是知道它是一个
string
然后做
string | boolean
而不仅仅是string。你仍然希望它是一个
keyof T
对吗?所以你可以做
const setValue=(newValue:T[keyof SubType])
。哦,我想你还得编辑你的
文本