TypeScript Version: 3.9.2
Search Terms:
Code
import { Select } from '@material-ui/core';
import React from 'react';
export function Repro({ SelectProps = {} }: { SelectProps?: Partial<React.ComponentProps<typeof Select>> }) {
return (
<Select value={'test'} {...SelectProps} />
);
}
Expected behavior:
No error, as value
in SelectProps
is optional and SelectProps
even defaults to an empty object.
Actual behavior:
'value' is specified more than once, so this usage will be overwritten.ts(2783)
Repro.tsx(7, 32): This spread always overwrites this property.
Playground Link:
none, as the playground does not seem to load the material-ui types
I noticed that this issue has 4.0 as milestone.
If 4.0 will have breaking changes and this bug was introduced in 3.9.2, it might be worth to consider making a fix for this bug in 3.9.x?
Same problem with
"@material-ui/core": "4.9.14",
"typescript": "3.9.2",
Reverting typescript to
"typescript": "3.8.3",
solves the problem.
I'm worried
Is belowCode Same Problem ?
Reverting typescript to
"typescript": "3.8.3",
solves the problem.
TypeScript Version: 3.9.2
https://www.typescriptlang.org/play/#code/MYewdgzgLgBADgUwE4XDAvDA3gKBvmAQwHMEAuGAJgAYAaPAgMwEsUoA5QgW3JgHIAUiAAWYPvQIwANoWiceFPgGEQIANbicAXwDcOUJBBSEAOikhiACkQpwASj37w0GMIsIM2BvhK8aEgkIkBEJFdgBNTUkoZihjRQBlQmMIGABZQjBfJCiCE3ybVDB6XRwnQ2MzC0s3UgcygxdahGbPXEl8k0LwAJ9SCn9vImDQ-gjc-Bi43j4klPTM7M1tPUajU3MrZub6oA
Code
```ts
const person = {
age: 25,
firstName: 'John',
lastName: 'Cook',
};
console.log(person);
//'age' is specified more than once, so this usage will be overwritten.(2783)
//input.ts(12, 5): This spread always overwrites this property.
const hoge = {
age: 20,
area: 'NY',
title: 'Sales Manager',
...person,
};
console.log(hoge);
//noProblem
const hogehoge = {
...person,
age: 20,
area: 'NY',
title: 'Sales Manager',
};
console.log(hogehoge);
@yamuun it's useless to write
const hoge = {
age: 20,
area: 'NY',
title: 'Sales Manager',
...person,
};
cause age won't be 20 in hoge. The spread always overwrites age property. So, it's ok to see "This spread always overwrites this property".
Should be fixed now
@DanielRosenwasser
3.9.3 not work
@weswigham ?
I'm not sure what you want from me, but:
3.9.3
, as indicated by the milestone and associated PR activity.3.9.3
, so didn't have the patch (his comment includes a reference to 3.9.2
)The issue is not resolved in 3.9.3
'addressNickName' is specified more than once, so this usage will be overwritten.
const data: Address = {
addressNickName: "Home",
streetName: "12th Street",
...payload,
};
@yogeshkotadiya without knowing the type of payload
, that message can be perfectly valid.
Does the type of payload
contain a non-optional addressNickName
property?
Then the message is correct and telling you that addressNickName: "Home"
will always be overridden by payload.addressNickName
so you can leave it out. In that case, it is not this bug.
@phryneas How would one then correctly do this when the return of the spread operator is not known or can't be known?
I know that all returns from my method will be prefixed and not actually match the previous properties but I don't know anything else about them, I wouldn't know how to type it.
axios.get(apiUrl + '/rlm/lists/' + self.state.id + '/entries', {
params: {
limit: this.state.rows,
offset: this.state.offset,
sort: this.getSortString(),
...this.getFilters()
},
withCredentials: true
})
getFilters = (): Object => {
let filtersArray = {} as any;
for(let [key, filterContent] of Object.entries(this.state.filters)) {
const filterKey = this.getHeaderKey(key);
filtersArray[filterKey] = this.getTypedFilterValue(key, filterContent.value.value);
}
return filtersArray;
}
'sort' is specified more than once, so this usage will be overwritten.ts(2783)
Editor.tsx(420, 9): This spread always overwrites this property.
@master117 You are spreading an array into an object spread there. That probably does not do what you want anyways.
I'm not even sure if that is defined JS behaviour.
Or rather, realistically, your JS is fine: but the return type of your method is Array<Object>
even though it should probably more look like Record<string, Object>
or Record<number, Object>
depending on what your keys are. It's definitely not an array though, since you are initializing it with {}
.
@phryneas Thanks for the fast reply. I made a typo, already fixed, the return is obviously Object not Object[].
The objects will be something like:
{ prefixedVariablename: any, prefixedVariablename2: any, prefixedVariablename3: any }
Which I wouldn't know how to type better than Object.
But it seems that removing the [] has resolved the error. So Object works fine, but Object[] doesn't. My problem is solved :)
I would have assumed something like [{key: value}, {key: value}] should work as well. Or rather throw a different type of error. As we then end with
params: {
limit: this.state.rows,
offset: this.state.offset,
sort: this.getSortString(),
{key: value},
{key: value}
},
Well, in this case the error was entirely accurate, as you were spreading an object with a defined sort
property (the array type) on top of an object with a sort
property. So that explains why you were getting it.
with a defined sort property (the array type)
Aaaah, of course, thank you again.
Funny thing about all this is sometimes I deliberately put a redundant property in a list - from a readability standpoint so it's clear what the intent was. Or as a quick fix when I had bad data that didn't conform to the typescript interface.
As others have said, can fix putting the ...spread
before the redundant properties.
FWIW, for people like me who find themselves on this thread, this problem is resolved in React by including the prop after a spread:
// Error:
// No error:
Which has a different runtime behaviour and exactly what the error is warning about.
In the first case, assuming the types were correct, iconOnly
would always be overwritten and never actually have that value. You probably don't intend that, which is why you get the error.
Same problem with 3.9.6
I have the exact same error with the test code below (I tagged the failing line with a '<===' comment): it says name
is specified more than once, but
customAttrs
is defined as Record<string, string>
. It can contain name
but it could not contain it either.genRandomVariantDef doesn't return the
name` keyWhere is name
specified more than once?
generateVariants: (upsMock: UPSMock, appId: string, count: number, customAttrs?: Record<string, string>[]) => {
const VARIANT_NAME_PREFIX = 'TEST VARIANT';
const variants = [];
const genRandomVariantDef = () => {
if (Math.round(Math.random() * 10) % 2 === 0) {
// Android variant
return {
type: 'android',
googleKey: '123456',
projectNumber: '1234556',
} as AndroidVariant;
} else {
// iOS variant
return {
type: 'ios',
production: false,
certificate: '123',
} as IOSVariant;
}
};
for (let i = 0; i < count; i++) {
variants.push(
upsMock.getImpl().createVariant(appId, {
name: `${VARIANT_NAME_PREFIX} ${i}`, // <======. 'name' is specified more than once, so this usage will be overwritten.
developer: 'admin',
variantID: Guid.raw(),
...(customAttrs ? customAttrs[i] : genRandomVariantDef()),
} as Variant)
);
}
return variants;
}
@ziccardi this looks legit. You should probably open a new Issue for that, as it is a different bug, even though it results in the same error message (this one has definitely been fixed).
Commenting on a closed issue has very low chances of this being resolved.
@phryneas 馃憤
@phryneas nvm. I leave it here for the log: when I was trying to create a simple snippet to replicate the issue, I noticed it was compiling fine. So the solution jumped to my mind. The error resides in the as AndroidVariant
and as IOSVariant
since they both inherit from Variant
whose definition is:
export interface Variant {
id: string;
variantID: string;
name: string;
description: string;
developer: string;
secret: string;
type: VariantType;
metadata?: {
activity: number;
deviceCount: number;
};
}
So, even if I don't really specify the name
two times, since it is defined as mandatory
in the base interface the compiler sees it as specified 2 times: my error, compiler was right.
I am using TS 3.9.6 and the issue still exist. see the simple code here:
interface Config {
Version:string;
publisher:string;
}
function send(config:Config) {
const finalConfig:Config = {
publisher: "name",
Version: "1.0",
...config,
}
}
Get error: TS2783: 'publisher' is specified more than once, so this usage will be overwritten.
When I do ...<any>config,
it pass, but I do want the static check which I can't get now.
@nisimjoseph The reason is that in the Config
interface the publisher
and Version fields are mandatory, so the compiler expects that the config
object already has those fields.
You can fix it by changing the Config
interface to:
interface Config {
Version?:string;
publisher?:string;
}
Thank you on the quick response.
Those values come from JS and not supposed to be Optional. they are mandatory , so I set the default values and then override the values with the ones I got from outside.
I also have some levels of objects inside the config with their own default values. I don't want to make it optional so the entire project will start to highlight fields as "optional" and I will need to add item existing check.
in addition, the Config has lots of keys/objects and it will create too many optional.
@nisimjoseph Yes, but the values you "get from the outside" are into an object that implements the Config
interface. Since the Config
interface says both publisher
and Version
are mandatory, the compiler supposes that config
always has both of them, thus always overwriting your 'default' values (hence the specified more than once
error)
@ziccardi I see your point, but this is not the case here.
the Config Interface, in this example, doesn't need to have all params from outside, only part of them and also it in JS and not TS like the source code.
The reason it is mandatory is I am filling all default (missing) values so the TS compiler will know all data exist for 100% of fields and won't gives errors on the optional fields all over the code.
Just think I have 20 configurations properties and I know not each client will need all of them and will set them in runtime in JS.
I just note it because I see it a lot as default values when init data.
@nisimjoseph If the object you are receiving does not contains all the mandatory fields, then it is not a Config
object. The easiest solution I see to do what you want is changing your code to
function send(config:Config) {
const finalConfig:Config = {
publisher: "name",
Version: "1.0",
...config as {},
}
}
Anyway, this is not a bug: compiler is right.
love the ...config as {},
solution. it working well, thank you.
@ziccardi I notice when I am using the following it gives me also nested object errors, which is better (use Partial
).
interface Config {
Version:string;
publisher:string;
}
function send(config:Partial<Config>) {
const finalConfig:Config = {
publisher: "name",
Version: "1.0",
...config,
}
}
And, I know the compiler is working fine, I was surprised it added in 3.8.x without a tsconfig way to disable it.
@nisimjoseph this is working for me:
interface Config {
Version:string;
publisher:string;
}
function send(config:Config) {
const finalConfig:Config = {
...config,
publisher: config.publisher || "name",
Version: config.Version || "1.0"
}
}
thank you @Vladyslav-Apukhtin, it will work in this way but I can drop the use of ...config,
.
value
in your mind ,but not for the props ;so you should put the {... SelectProps}
before the value
you writevalue
property you write which can't change anything@Talent-Rain
SelectProps
is a Partial property. This means every of it properties can be there, but it could also be not. It will not necessarily be overridden, but it could be. Which is fine, and a case where that error message should not have shown up.Could we please stop posting unrelated stuff here? This issue has been fixed and everything after that is either worthy of a new ticket or not of interest any more, as this bug has been fixed.
I agree 馃憜
Most helpful comment
I noticed that this issue has 4.0 as milestone.
If 4.0 will have breaking changes and this bug was introduced in 3.9.2, it might be worth to consider making a fix for this bug in 3.9.x?