typescript - رشته قالبی template با متغیر باعث میشود: آرگومان از نوع s قابل انتساب به پارامتر از نوع نباشد


این یک سؤال پیوسته با این سؤال است: آیا راهی برای تمایز بین رشته و رشته قالب وجود دارد

type Data = {
  arrayObject: {
    firstName: string;
    lastName: string;
  }[];
};

type ArrayElementType<A> = A extends readonly (infer T)[] ? T : never

type TupleOf<A extends number, R extends number[] = []> =
  R['length'] extends A ? R : TupleOf<A, [...R, R['length']]>;

type Integers = TupleOf<1>;

type Indexes = Integers[number];

type PathFinder<T, Key extends keyof T = keyof T> = Key extends string
    ? T[Key] extends (string | boolean | number | symbol)[]
      ? `${Key}.${Indexes}`
      : T[Key] extends object[]
        ? `${Key}.${Indexes}.${PathFinder<ArrayElementType<T[Key]>>}`
        : T[Key] extends Record<string, any>
          ? `${Key}.${PathFinder<T[Key]>}`
          : Key
    : never;

function register<Data>(name: PathFinder<Data>) {
  console.log(name)
}

const index = '0'

register(`arrayObject.${index}.lastName` as const); // works for TS 4.1
register(`arrayObject.${index}.lastName`); // works for TS 4.2

const i:number = '0'
register(`arrayObject.${index}.lastName` as const); fail
register(`arrayObject.${index}.lastName`); // fail

با این حال، زمانی که شاخص پویا است و نوع آن number خواهد بود، مثلا میتواند منجر به یک for loop شود، این دوباره نوع را میشکند. آیا راهی وجود دارد که نوع number یا string را در نوع literal رشته قالبی قرار داد؟

زمین بازی

پاسخ شماره ۱

یک هک کوچک StringNumber وجود دارد.

راهحل :

const index = '0';

const lit = `arrayObject.${index}.lastName`;
// const lit: string

const litAsConst = `arrayObject.${index}.lastName` as const;


namespace Orig {
  type Data = {
    arrayObject: {
      firstName: string;
      lastName: string;
    }[];
    firstName: string;
  };

  type ArrayElementType<A> = A extends readonly (infer T)[] ? T : never

  type TupleOf<A extends number, R extends number[] = []> =
    R['length'] extends A ? R : TupleOf<A, [...R, R['length']]>;

  type Integers = TupleOf<1>;

  type Indexes = Integers[number];

  type PathFinder<T, Key extends keyof T = keyof T> = Key extends string
    ? T[Key] extends (string | boolean | number | symbol)[]
    ? `${Key}.${Indexes}`
    : T[Key] extends object[]
    ? `${Key}.${Indexes}.${PathFinder<ArrayElementType<T[Key]>>}`
    : T[Key] extends Record<string, any>
    ? `${Key}.${PathFinder<T[Key]>}`
    : Key
    : never;

  function register<Data>(name: PathFinder<Data>) {
    console.log(name)
  }

  type StringNumber<T extends number> = `${T}` // <---- my solution is here

  const index: StringNumber<0> = '0'
  const result = register<Data>(`arrayObject.${index}.lastName` as const);
  register<Data>('firstName');
}