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');
}
