Typescript 按名称以字符串形式返回对应的输入类型(字符串模板文本) type generatarrayKeyType=`${T}.${K}`; 类型PathImpl= 键扩展字符串 ? T[Key]扩展只读未知[]? GeneratorArrayKeyType :T[键]扩展记录 ? | `${Key}.${PathImpl&string}` |`${Key}.${Exclude&string}` :从不 :从不; PathImpl2型=PathImpl | keyof T; type Path=PathImpl2扩展字符串| keyof T?路径2:keyof T; 类型路径值= P扩展了`${inferkey}.${inferrest}` ? 键扩展了keyt的键 ? Rest扩展路径 ? 路径值 :T[Key]扩展(推断U)[] ? U :从不 :从不 :P扩展了T的键 ? T[P] :从不; 类型DeepNest={ 测试:字符串[], } 类型数据={ 数组:字符串[], 测试:字符串, 最后:弦,, 文件:文件[], 日期:日期[], 嵌套:{ 测试:字符串, }, 深度:{ 数组:DeepNest[] } } 类型paths=路径; 功能测试(名称:B):路径值{ 将“”返回为任意值; } 常量输出=测试(“测试”)

Typescript 按名称以字符串形式返回对应的输入类型(字符串模板文本) type generatarrayKeyType=`${T}.${K}`; 类型PathImpl= 键扩展字符串 ? T[Key]扩展只读未知[]? GeneratorArrayKeyType :T[键]扩展记录 ? | `${Key}.${PathImpl&string}` |`${Key}.${Exclude&string}` :从不 :从不; PathImpl2型=PathImpl | keyof T; type Path=PathImpl2扩展字符串| keyof T?路径2:keyof T; 类型路径值= P扩展了`${inferkey}.${inferrest}` ? 键扩展了keyt的键 ? Rest扩展路径 ? 路径值 :T[Key]扩展(推断U)[] ? U :从不 :从不 :P扩展了T的键 ? T[P] :从不; 类型DeepNest={ 测试:字符串[], } 类型数据={ 数组:字符串[], 测试:字符串, 最后:弦,, 文件:文件[], 日期:日期[], 嵌套:{ 测试:字符串, }, 深度:{ 数组:DeepNest[] } } 类型paths=路径; 功能测试(名称:B):路径值{ 将“”返回为任意值; } 常量输出=测试(“测试”),typescript,Typescript,我想从字符串中提取类型。上面的代码与名称查找test一起工作,但是,返回类型将返回所有类型,而不是目标输入类型。欢迎提出任何建议和想法 如果我理解正确,您的问题与递归模板文字类型别名的行为几乎没有关系,因此为了生成一个最小的可复制示例,让我们抛开所有这些定义,只需检查以下函数并尝试调用它: type GenerateArrayKeyType<T extends string, K extends number> = `${T}.${K}`; type PathImpl<T,

我想从字符串中提取类型。上面的代码与名称查找
test
一起工作,但是,返回类型将返回所有类型,而不是目标输入类型。欢迎提出任何建议和想法


如果我理解正确,您的问题与递归模板文字类型别名的行为几乎没有关系,因此为了生成一个最小的可复制示例,让我们抛开所有这些定义,只需检查以下函数并尝试调用它:

type GenerateArrayKeyType<T extends string, K extends number> = `${T}.${K}`;

type PathImpl<T, Key extends keyof T> =
  Key extends string
  ? T[Key] extends readonly unknown[] ?
    GenerateArrayKeyType<Key, 0 | 1>
    : T[Key] extends Record<string, any>
      ? | `${Key}.${PathImpl<T[Key], Exclude<keyof T[Key], keyof any[]>> & string}`
        | `${Key}.${Exclude<keyof T[Key], keyof any[]> & string}`
      : never
  : never;

type PathImpl2<T> = PathImpl<T, keyof T> | keyof T;

type Path<T> = PathImpl2<T> extends string | keyof T ? PathImpl2<T> : keyof T;

type PathValue<T, P extends Path<T>> =
  P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
    ? Rest extends Path<T[Key]>
      ? PathValue<T[Key], Rest>
      : T[Key] extends (infer U)[] 
        ? U
        : never
    : never
  : P extends keyof T
    ? T[P]
    : never;

type DeepNest = {
  test: string[],
}

type Data = {
  array: string[],
  test: string,
  last: string,
  file: File[],
  date: Date[],
  nest: {
    test: string,
  },
  deep: {
    array: DeepNest[]
  }
}

type Pathes = Path<Data>;


function test<T, B extends Path<T> = Path<T>>(name: B): PathValue<T, B> {
    return '' as any;
}


const output = test<Data>('test')
当然,这是失败的,因为没有任何东西可以推断
T
。另一方面,如果使用尖括号调用函数,编译器将不会为您推断任何类型参数:

它可以工作,但有一个缺点,就是您给函数指定了一个类型为
T
的值,它在运行时实际上并不需要这个值

或者,您可以使用带有单个类型参数的泛型函数手动指定,这将返回带有另一个类型参数的泛型函数以推断:

declare function minTestDummy<T, K extends keyof T>(dummyT: T, k: K): T[K];

minTestDummy(null! as Data, "test").toUpperCase(); // okay
声明函数minTestCurry():(k:k)=>T[k];
minTestCurry()(“测试”).toUpperCase();//可以
const dataTest=minTestCurry();
dataTest(“test”).toUpperCase();//可以
这也可以工作,但有一个缺点,就是您在运行时调用了一个额外的函数。就我个人而言,我更喜欢咖喱,因为它将手工部分与推理部分分开,但任何一种方法都应该有效


将当前解决方案转换回问题中的代码如下所示:

declare function minTestCurry<T>(): <K extends keyof T>(k: K) => T[K];

minTestCurry<Data>()("test").toUpperCase(); // okay

const dataTest = minTestCurry<Data>();
dataTest("test").toUpperCase(); // okay
declare function test():(名称:B)=>PathValue;
常量输出=test()('test')


非常感谢您!这个答案是如此全面和详细。
minTest<Data, "test">("test"); // minTest<Data, "test">()
minTest<Data>("test") // minTest<Data, keyof Data>()
declare function minTestDummy<T, K extends keyof T>(dummyT: T, k: K): T[K];

minTestDummy(null! as Data, "test").toUpperCase(); // okay
declare function minTestCurry<T>(): <K extends keyof T>(k: K) => T[K];

minTestCurry<Data>()("test").toUpperCase(); // okay

const dataTest = minTestCurry<Data>();
dataTest("test").toUpperCase(); // okay
declare function test<T>(): <B extends Path<T>>(name: B) => PathValue<T, B>;
const output = test<Data>()('test')