简体   繁体   中英

Typescript. Some object keys in array with foreach method and rewrite keys

I have this object:

const task = ref<Task>({
  name: '',
  description: '',
  type: undefined,
  level: 'tactic',
  participants: undefined,
  stages: undefined,
});

export interface Task extends CommonEntity {
  description?: string;
  type?: TaskType;
  level?: EntityLevel;
  participants?: number;
  stages?: TaskTypeStage[];
  questions?: TaskQuestion[];
  materials?: TaskMaterial[];
  indicators?: TaskIndicator[];
  duration?: number;
  images?: [];
  program_id?: number;
  roles?: TaskRole[];
  tables?: TaskTable[];
  competences?: TaskCompetence[];
  task_type_id?: number;
}

export interface CommonEntity {
  id?: number;
  name?: string;
  created_at?: string;
  updated_at?: string;
  errors?: ApiErrors;
  delete?: boolean;
  isTemporaryIdAdded?: boolean;
}

in some method I want to change task objects.

function handleSubmit() {
    task.value.materials = removeTemporaryIdFromArray<TaskMaterial>(task.value.materials);

    task.value.questions = removeTemporaryIdFromArray<TaskQuestion>(task.value.questions);

    task.value.roles = removeTemporaryIdFromArray<TaskRole>(task.value.roles);

    task.value.tables = removeTemporaryIdFromArray<TaskTable>(task.value.tables);
}

export function removeTemporaryIdFromArray<T>(
  entity: TaskMaterial[] | TaskQuestion[] | TaskRole[] | TaskTable[] | undefined
) {
  if (entity) {
    return entity
      .filter((item) => !item.delete || !item.isTemporaryIdAdded)
      .map((item) => Object.assign({}, removeTemporaryId<T>(item)));
  }
}

export function removeTemporaryId<T>(item: CommonEntity): T {
  const { id, isTemporaryIdAdded, ...rest } = item;

  return isTemporaryIdAdded ? (rest as T) : { id, ...(rest as T) };
}

I'm watching a way to change function handleSubmit without write four times task.value[key] = removeTemporaryIdFromArray . To something with ['objectKey1', 'objectKey2'].forEach(key => object[key] = changeFunction(object[key]) )

I'm cheating and write function below. And ts don't see mistakes

(['materials', 'questions', 'roles', 'tables'] as (keyof typeof task.value)[]).forEach((key) => {
      task.value[key] = removeTemporaryIdFromArray<typeof key>(task.value[key] as TaskMaterial[]) as undefined;
    });

What I don't understand is why task.value[key] is waiting undefined as value. I understand, that solution it is not quite right. Can you suggest a better solution?

I think the affectation is not ok

task.value[key] = removeTemporaryIdFromArray<typeof key>(task.value[key] as TaskMaterial[]) as undefined;

because removeTemporaryIdFromArray can not choose between various types (TaskMaterial[] | TaskQuestion[] | TaskRole[] | TaskTable[] | undefined) when set task.value[key].

I think you can work around with removeTemporaryIdFromArray_InPlace.

Here is my proposal (little verbose may be), and it is difficult to test without context. But typescript say ok.

// helper type
type Writable<T> = { -readonly [K in keyof T]: T[K] };
// your properties to loop on
const KeyNames = ["materials", "questions", "roles", "tables"] as const;
// type helper with only the 'good' keys, never undefined (but we do not care.)
type SubPropertyTask = Required<Pick<Task, typeof KeyNames[number]>>;
// your properties to loop on transformed in an array of keys, that you can loop on really.
const WriteableKeyNames = KeyNames as Writable<typeof KeyNames> as Array<keyof SubPropertyTask>;

// your new submit
function handleSubmitV3() {
    // loop on key
    WriteableKeyNames.forEach((key: keyof SubPropertyTask) => {
        // the call wich is not so trivial
        // < type of first element of the array (because we know its array)
        // need also type of the key, taht can not be infered.
        removeTemporaryIdFromArrayInplace<SubPropertyTask[typeof key][0], typeof key>(task[key], key);
    });
}
// T might be written like this : T extends TaskQuestion | TaskMaterial | TaskRole | TaskTable
// but if you need to add other property, name should be enougth
export const removeTemporaryIdFromArrayInplace = <T, E extends keyof SubPropertyTask>(entity: Array<T> | undefined, e: E): void => {
    // do your own filter/
    const filteredEntity = entity?.filter((taskObject: T) => taskObject);

    // set up the new value, which would work, with this particlar cast.
    // Because at this time typeof task[e] is same as T and as  SubPropertyTask[E] | undefined
    // ... I think.
    task[e] = filteredEntity as SubPropertyTask[E] | undefined;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM