Как решить проблему с типизацией getValue?

Можно ли типизировать не приводя к типу Getter?
Требования:
- Key должен быть типизирован, то есть нельзя указать key, который не принадлежит объекту
- Возвращаемое значение (одиночное значение или массив) должно на уровне типов конкретизироваться через параметр isIncludeKey.
Sandbox
type ObjectType = Record<string, unknown>;

type OnlyString<T> = T extends string ? T : never;

type GetObjectKeys<T extends ObjectType> = OnlyString<keyof T>;

type GetObjectValues<T extends ObjectType> = T[GetObjectKeys<T>];

type Getter<T extends ObjectType, Flag extends boolean> = (
  key: GetObjectKeys<T>
) => Flag extends true ? Array<GetObjectValues<T>> : GetObjectValues<T>;

const getValue =
  <TTargetObject extends ObjectType, TFlag extends boolean>(
    obj: TTargetObject,
    isIncludeKey?: TFlag
  ): Getter<TTargetObject, TFlag> =>
  (key) => {
    if (!isIncludeKey) {
      return obj[key];
    }

    return Object.entries(obj).reduce<GetObjectValues<TTargetObject>[]>(
      (result, pair) => {
        const [pairKey, pairValue] = pair;
        if (pairKey.includes(key)) {
          return [...result, pairValue] as GetObjectValues<TTargetObject>[];
        }

        return result;
      },
      []
    );
  };

const getter = getValue({ a: 1, b: 2, c: 3, d: 4, e: 5 }, true);
console.log(getter("a"));

/*
Type '(key: OnlyString<keyof TTargetObject>) => TTargetObject[OnlyString<keyof TTargetObject>] | GetObjectValues<TTargetObject>[]' is not assignable to type 'Getter<TTargetObject, TFlag>'.
*/
  • Вопрос задан
  • 116 просмотров
Пригласить эксперта
Ответы на вопрос 1
WblCHA
@WblCHA
https://www.typescriptlang.org/play?#code/C4TwDgpg...

type ExtractValuesFromSubKeys<T extends object, SK extends string, K extends keyof T & string = keyof T & string> = K extends `${string}${SK}${string}` ? T[K] : never

function getValue<T extends object>(obj: T, isIncludeKey?: false): <K extends keyof T>(key: K) => T[K]
function getValue<T extends object>(obj: T, isIncludeKey: true): <SK extends string>(subKey: SK) => ExtractValuesFromSubKeys<T, SK>[]
function getValue<T extends object>(obj: T, isIncludeKey?: boolean) {
  return (keyOrSubKey: string) => {
    if (!isIncludeKey) {
      const key = keyOrSubKey as keyof T

      return obj[key];
    }

    const subKey = keyOrSubKey

    return Object.keys(obj).filter(key => key.includes(subKey)).map(key => obj[key as keyof T])
  }
}

const getter = getValue({ aaa: 1, bbb: '2', ccc: [3], abc: null }, true);

const ttt1 = getter("a") // (number | null)[]
const ttt2 = getter("bb") // string[]
const ttt3 = getter("ccc") // number[][]
const ttt4 = getter("unknown") // never[]
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы