-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Bug: [consistent-indexed-object-style] shouldn't suggest Record
when extending an another type
#9068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hmmm, the comments in this PR tell an interesting story for this case: #3009 This behavior seems to be intentional... The case you bring up falls where those two entirely separate behaviors interact |
I'm surprised this has never come up before! Sometimes I just need to be able to access properties by index. This particular case is for some custom error logging where I want to grab all properties off of an incoming error, but exclude a few by name. TS complained at me for not having an index signature, so I added this. Here's my real world use case for the type I ran into this on export interface IIndexableAxiosError extends AxiosError {
[key: string]: unknown;
} |
It's generally very, very rare that you want to add an unbounded indexer to an existing type! Or a safer way to do things would be eg declare const key: string;
declare const base: AxiosError;
if (base.hasOwnProperty(key)) {
base[key as keyof typeof base];
} I don't think there's a really compelling, broadly-applicable reason to allow this within the rule. |
ok, fair enough. I've updated my code to grab the value using However, given my original code it still feels kinda wrong to recommend that an extended interface with an added index be recommended to use as as |
ok, so I've actually run into this issue yet again in a slightly different way. I have a large "base" interface and then I make several smaller and specific interfaces with Here's a playground link for everything below. I could go this route: interface ILargeBaseA {
foo: string;
bar: number;
baz: boolean
qux: string[];
zop: null | string;
}
interface IStringsOnlyA extends Pick<ILargeBaseA, 'foo' | 'qux' | 'zop'> {
[key: string]: string | string[] | null;
}
function getValA(obj: IStringsOnlyA, key: string) {
return obj[key];
} which works great and the return type of Ok, I'll lean into that and move the indexing to the base class and use a interface ILargeBaseB {
[key: string]: string | string[] | boolean | number | null;
foo: string;
bar: number;
baz: boolean
qux: string[];
zop: null | string;
}
type IStringsOnlyB = Pick<ILargeBaseB, 'foo' | 'qux' | 'zop'>;
function getValB(obj: IStringsOnlyB, key: string) {
return obj[key];
} ESLint no longer complains, but now I lose my index signature it it just infers an
as far as I can tell, my only workaround here is to disable this rule since it just seems flat out wrong my my use case here. a possible workaround I might be able to do is use interface ILargeBaseC {
[key: string]: string | string[] | boolean | number | null;
foo: string;
bar: number;
baz: boolean
qux: string[];
zop: null | string;
}
type IStringsOnlyC = Pick<ILargeBaseC, 'foo' | 'qux' | 'zop'>;
function getValC(obj: IStringsOnlyC, key: keyof IStringsOnlyC) {
return obj[key];
} |
Why not something like type IStringsOnlyC = Pick<ILargeBaseC, 'foo' | 'qux' | 'zop'> & Record<string, string | string[] | null>; Or more generally type IndexableThing = T & Record<string, values of T>; What's wrong with an intersection here? |
I do not use intersection types often, thanks for the reminder. ok, good call there. but again, perhaps the rule could make more intelligent recommendations/fixes for cases like these? |
I think:
All the above are what the rule considers as "indexed object". However there are a few other types: type A = {
x: 1;
[y: number]: 2;
};
interface B {
x: 1;
[y: number]: 2;
}
type C = { x: 1 } & Record<number, 2>;
interface Base {
x: 1;
}
interface D extends Base {
[y: number]: 2;
} To me, the rule only reporting |
Marking as |
3 months later and this request has received 0 community reactions. |
Before You File a Bug Report Please Confirm You Have Done The Following...
Playground Link
https://typescript-eslint.io/play/#ts=5.4.3&fileType=.ts&code=JYOwLgpgTgZghgYwgAgLIE8DCBXAzmAewFsAVAC1AHNkBvAWAChlkYCCAuZfKKgbkeYAjOFE7c%2BA5MIBenek2bIAjtgAeYsDxCUA2gF1%2BCgL6MTDRqEixEKAJIgAJhFVxBAGwjkqAOQK3cuNgQuMjOkI4hGDj4xF7atJI6ANYQ6BpalHqc2CBJIAQA7iCGzARgZNCcINhEgtCGZhbg0PBIyPZOLu6eFNoA6sDl-oHBoarhDpFYeISkvdTyzMmp6VRZyDl5hcWmQA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQAIBcBPABxQGNoBLY-AWhXkoDt8B6MgeyeUuX0Ra1mAE0QAPRMNocARgCtEZOn0JJ0URNGgdokcGAC%2BIA0A&tsconfig=&tokens=false
Repro Code
ESLint Config
tsconfig
No response
Expected Result
Since I am extending an object I would expect no errors.
Actual Result
Error with "A record is preferred over an index signature." even though it's extending an existing type. Replacing this with a
Record
is not the equivalent codeAdditional Info
No response
The text was updated successfully, but these errors were encountered: