Как мне создать тип объекта, который должен иметь все ключи типа массива - PullRequest
0 голосов
/ 01 августа 2020

Я пытаюсь создать общий c класс для определения ресурса, который имеет грубые операции. Моя конечная цель - иметь общий экземпляр класса c, который можно использовать для создания сервисов , срезов редуктора и компонентов для автоматического отображения данных, фильтрации и разбивать на страницы определенный ресурс. Для текущего варианта использования это то, что я хочу определить

Resource City имеет родительский штат и страну CityInstance.getPath должен принимать ids для state и country и вернуть / country / 1 / states / 1 / cities

export default class Resource {
  name: string;
  path: string;
  parents: string[];

  constructor(arg: { name: string; path: string; parents: string[] }) {
    this.name = arg.name;
    this.path = arg.path;
    this.parents = arg.parents;
  }
//I want the argument parentIds to include every single element of parents and their ids
  getBaseUrl(parentIds: {[parentName:string]=> id}){
       const parentPath = this.parents.map(p=>`/${p}/${parentIds[p]}`).join("");

       return `${parentPath}/${this.path}`
  }

}


const resource = new Resource({name:"city", parents: ["countries", "states"]})
//The next call should force me to supply the object of ids  and should not allow calls without them such as this one

resource.getBaseUrl({}) // Typescript should not allow


resource.getBaseUrl({country: 1}) // Typescript should not allow. 


//This would be valid since it supplies both state and country

resource.getBaseUrl({country: 1, state:2});

Я понимаю, что машинописный текст не может предсказать, что значения времени выполнения будут и поэтому не могут выводить типы только из кода. Я пробовал создавать родителей как типаж, но не знаю, как с этим работать

class Resource<ResourceType,  Parents extends string[] =[] > {
    name:string
    resourceUrlName:string
    
    constructor(name:string,resourceUrlName:string){
        this.name=name
        this.resourceUrlName = resourceUrlName
    }
    //How do I specify that the indexer should be a part of parents array type
    generateBasePathForCollection(parents: {[name: keyof Parents} : number}){
      // How do I get all the members of the Parents Array

    }
}

Ответы [ 2 ]

1 голос
/ 01 августа 2020

Насколько я знаю, вы не можете создавать сопоставленные типы с значениями массива. Как насчет того, чтобы немного изменить подпись, чтобы помочь вам? Вы можете выполнить сопоставленный тип ключей объекта.

type WithParents<T extends Record<string, number>> = {
  baseUrl?: string;
  name: string;
  parents: T;
};

class Resource<T extends Record<string, number>> {
  baseUrl?: string;
  name: string;
  parents: T;

  constructor(args: WithParents<T>) {
    this.baseUrl = args.baseUrl;
    this.name = args.name;
    this.parents = args.parents;
  }

  getBaseUrl(input: { [P in keyof T]: string | number }) {
    const parts = Object.keys(this.parents).sort(k => this.parents[k]);
    return [this.baseUrl, ...parts.map(key => `${key}/${input[key]}`)]
      .filter(Boolean)
      .join("/");
  }
}

Важными частями здесь являются универсальные шаблоны для класса и типа WithParents, а также сопоставленный тип arg (на основе аргумент generi c класса) в getBaseUrl. Вот документы по сопоставленным типам: https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped -типы

Затем

const x = new Resource({ name: "string", parents: { countries: 1, state: 2 } });

console.log(x.getBaseUrl({ countries: 1, state: 2 })); // countries/1/state/2 
console.log(x.getBaseUrl({ state: 1, countries: 2 })); // countries/2/state/1 

const y = new Resource({ name: "string", parents: { state: 1, countries: 2 } });

console.log(y.getBaseUrl({ countries: 1, state: 2 })); // state/2/countries/1 
console.log(y.getBaseUrl({ state: 1, countries: 2 })); // state/1/countries/2 

Вы можете сделать это полезным, например { countries: "number" }, а затем выполнить некоторую проверку об этом, пока вы там. В его нынешнем виде вы можете указать любое значение для значения записи в записи, и это будет работать.

Изменить: обновлено для поддержания порядка. Поскольку начальные значения для parents не используются, мы будем использовать их значение для обозначения порядка (начиная с 1).

0 голосов
/ 01 августа 2020

Обновлено , чтобы parents

parents предоставляется как объект того же типа parentIds, что и T. Значения parents не имеют значения, кроме их типов. Порядок ключей сохраняется.

export default class Resource<T extends { [parentName: string]: number }> {
    name: string;
    path: string;
    parents: T;

    constructor({ name, path, parents }: { name: string; path: string; parents: T; }) {
        this.name = name;
        this.path = path;
        this.parents = parents;
    }
    // You want the argument parentIds to include every single element of parents and their ids
    getBaseUrl(parentIds: T) {
        const parentPath = Object.keys(this.parents).map(p => `/${p}/${parentIds[p]}`).join("");

        return `${parentPath}/${this.path}`
    }

}


const resource = new Resource({ name: "city", path: "dont-forget-path", parents: { country: 0, state: 0 } })
//The next call should force me to supply the object of ids  and should not allow calls without them such as this one

resource.getBaseUrl({}) // Typescript should not allow


resource.getBaseUrl({ country: 1 }) // Typescript should not allow. 


//This would be valid since it supplies both state and country

resource.getBaseUrl({ country: 1, state: 2 });

Обратите внимание, что {[parentName:string]=> id} недопустимо, поскольку => должно быть :, а id должно быть типом.

Более того , при вызове конструктора не забудьте поставить path.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...