TypeScript:将对象项映射到类型

TypeScript:将对象项映射到类型,typescript,generics,type-inference,Typescript,Generics,Type Inference,作为一个typescript新手,我甚至很难理解这个问题,所以请耐心听我说 我试图创建字符串和valueObject(作为一种类型)的key=>[string+valueObject接口]映射,然后创建一个函数,该函数基于传递的键强制valueObject接口 我觉得最好用一个例子来解释: // This is an pseudo example stub, not actually working type ReplaceableWith<T> = string; //

作为一个typescript新手,我甚至很难理解这个问题,所以请耐心听我说

我试图创建字符串和valueObject(作为一种类型)的key=>[string+valueObject接口]映射,然后创建一个函数,该函数基于传递的键强制valueObject接口

我觉得最好用一个例子来解释:

// This is an pseudo example stub, not actually working

type ReplaceableWith<T> = string;
//                   ^ the type I'd like to enforce as the argument

const templates = {
  // templateId    // template               // define somehow the interface required for this template
  'animal.sound': 'A {animal} goes {sound}' as ReplaceableWith<{ animal: string; sound: string}>
};

function renderTemplate(
  templateId , // must be a key of templates
  params // must match value object type, based on templateId
): string {
  let rendered = templates[templateId];
  for (const [key, value] of Object.entries(params)) {
    // replace keys from template with values
    rendered = rendered.replace('{' + key + '}', value);
  }
  return rendered;
}

const a = renderTemplate('animal.sound', { animal: 'Dog', sound: 'woof' })
//    ^ a = 'A Dog goes woof'
const b = renderTemplate('animal.sound', { name: 'Some' });
//                                         ^ should throw TS error
虽然这样做有效,但有两个问题:

  • 我必须定义两次键(在接口和实例中)
  • 参数接口和消息是分开的,理想情况下它们应该在一起

  • 请查看以下代码:

    const枚举动物{
    声音=‘动物的声音’,
    睡眠='动物.睡眠',
    牧群='动物.牧群',
    思考='动物,思考',
    }
    类型TemplateKeys={
    [动物.声音]:{动物:弦;声音:弦};
    [Animal.sleep]:{location:string};
    [动物群]:{expectedCount:number;available:number};
    [动物。思考]?:字符串;
    };
    常量模板:记录
    
    这是

    的链接,请查看以下代码:

    const枚举动物{
    声音=‘动物的声音’,
    睡眠='动物.睡眠',
    牧群='动物.牧群',
    思考='动物,思考',
    }
    类型TemplateKeys={
    [动物.声音]:{动物:弦;声音:弦};
    [Animal.sleep]:{location:string};
    [动物群]:{expectedCount:number;available:number};
    [动物。思考]?:字符串;
    };
    常量模板:记录
    这是你的链接

    type TemplateKeys = {
      'animal.sound': { animal: string; sound: string };
      'animal.sleep': { location: string };
      'animal.herd': { expectedCount: number; available: number };
      'animal.think': undefined;
    };
    
    const templates: {[key in keyof TemplateKeys]: string} = {
      'animal.sound': '{animal} goes {sound}',
      'animal.sleep': 'It sleeps in {location}',
      'animal.herd': 'There is {available} animals out of {expectedCount}',
      'animal.think': 'Its thinking'
    };
    
    function renderTemplate<K extends keyof TemplateKeys>(key: K, params?: TemplateKeys[K]): string {
      if (params !== undefined) {
        //@ts-ignore
        return Object.entries(params).reduce((previousValue: string, [param, value]: [string, any]) => {
          return previousValue.replace('{' + param + '}', value);
        }, templates[key]);
      }
      return templates[key];
    }
    
    console.log(renderTemplate('animal.sound', { animal: 'Dog', sound: 'woof' }));
    console.log(renderTemplate('animal.sleep', { location: 'a hut' }));
    console.log(renderTemplate('animal.herd', { expectedCount: 20, available: 10 }));
    console.log(renderTemplate('animal.think'));
    
    [LOG]: Dog goes woof 
    [LOG]: It sleeps in a hut 
    [LOG]: There is 10 animals out of 20 
    [LOG]: Its thinking