英文:
Is there any wrong with my interface create and assignment?
问题
以下是您提供的代码的中文翻译部分:
我是新手,正在使用React库和TypeScript以及next.js构建Web应用程序。
我正在尝试构建一个待办事项列表Web应用程序。
我创建了一个用于收集列表数据的接口,如下所示:
interface listData {
text: string,
isDone: boolean,
viewMode?: { display: '' },
editMode?: { display: 'none' },
}
const [list, setList] = useState<listData[]>([]);
const addList = (item: any) => {
if (item !== "") {
const newList = [...list, { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }];
localStorage.setItem('listData', JSON.stringify([...list, { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }]));
setList(newList);
};
}
当我使用setList(newList)
时,我在VSCode下面收到以下错误:
无法分配给类型 'SetStateAction<listData[]>'.
类型 '(listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; })[]' 无法分配给类型 'listData[]'.
类型 'listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }' 无法分配给类型 'listData'.
类型 '{ text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }' 无法分配给类型 'listData'。
在这些类型之间,'viewMode.display' 的类型不兼容。
类型 'string' 无法分配给类型 '""'.ts(2345)
viewMode和editMode用于显示CSS以确定显示哪个标签,例如,当用户按下编辑按钮时,viewMode.display为'none',否则editMode.display为'none'。
现在我在addList函数中遇到了关于赋值的问题,请查看图片。
我不确定addList函数中的问题是什么。希望有人能告诉我我哪里错了,真的非常感谢。
英文:
I'm new in to build a web with React Libray, and use next.js with typscript to create the web app.
And I'm trying to build a todo list web app.
I create an interface for collet the list data like below:
interface listData {
text: string,
isDone: boolean,
viewMode?: { display: '' },
editMode?: { display: 'none' },
}
const [list, setList] = useState<listData[]>([]);
const addList = (item: any) => {
if (item !== "") {
const newList = [...list, { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }];
localStorage.setItem('listData', JSON.stringify([...list, { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }]));
setList(newList);
};
}
I got the error on vscode down below when I setList(newList):
Argument of type '(listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; })[]' is not assignable to parameter of type 'SetStateAction<listData[]>'.
Type '(listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; })[]' is not assignable to type 'listData[]'.
Type 'listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }' is not assignable to type 'listData'.
Type '{ text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }' is not assignable to type 'listData'.
The types of 'viewMode.display' are incompatible between these types.
Type 'string' is not assignable to type '""'.ts(2345)
The viewMode and editMode is build for display css to show which tag will be show,
like when user press the edit button, then the viewmode.display: 'none', otherwise editMode.display: 'none'.
And now I got a problem about addList assignment problem, please look at the image.
I not sure what the prolem is now in addList function when I setList(newList).
Hope someone can tell me where am I worng, and really thanks for it.
return (
<>
<div className={styles.bodyOuterDiv} >
...
<ul >
{list.map((item, index) => {
return (
<li key={index} className={styles.todoListOuterLi} >
<div className={item.isDone ? styles.todoListItemsWithIsDone : styles.todoListItems} style={item.viewMode}>
{item.text}
</div>
<textarea className={styles.todoListTextArea} value={item.text} onChange={(e) => { setItem(e.target.value) }} style={item.editMode} />
...
</li>
);
})}
</ul>
...
</div>
</>
);
答案1
得分: 1
问题在于,因为您没有在 newList
或其中的新对象上添加类型注释,TypeScript 推断 这些属性的类型,将它们推断为 string
类型(而不是字符串文字类型 ""
和 "none"
),但您的接口需要字符串文字类型 ""
和 "none"
。
修复的方法有几种。可能最直接的是告诉 TypeScript newList
是 listData[]
类型(但继续阅读,还有一些其他问题):
const addList = (item: any) => {
if (item !== "") {
const newList: listData[] = [ // <===========
...list,
{ text: item, isDone: false, viewMode: { display: "" }, editMode: { display: "none" } },
];
localStorage.setItem(
"listData",
JSON.stringify([
...list,
{
text: item,
isDone: false,
viewMode: { display: "" },
editMode: { display: "none" },
},
])
);
setList(newList);
}
};
顺便说一下,item
参数的类型应该是 string
,而不是 any
:
const addList = (item: string) => {
// −−−−−−−−−−−−−−−−^^^^^^
将其设置为 any
会使您能够执行 addList(42)
,这将在列表中放置一个具有 text: 42
的对象,这不是类型所说的内容。
另外,还有一些其他事情:
- 每当基于现有状态设置状态时,最好使用状态设置器的回调形式,而不是依赖于
addList
闭合的list
的副本,因为它可能已经过时。 - 没有必要重复添加新项的代码,只需在本地存储时使用
newList
。
所以:
const addList = (item: string) => {
if (item !== "") {
setList((list) => {
const newList: listData[] = [
...list,
{
text: item,
isDone: false,
viewMode: { display: "" },
editMode: { display: "none" },
},
];
localStorage.setItem("listData", JSON.stringify(newList));
return newList;
});
}
};
使用回调形式还可以让您对 addList
进行记忆化,这在您要将其传递给的组件进行记忆化时可能会有用。给予组件每次一个新的 addList
强制它重新渲染,而如果 addList
是稳定的(因为它被记忆化了),组件可能可以避免重新渲染。不过是否要记忆 addList
取决于您要传递给它的组件。 (有关 useCallback
的更多信息,请参阅我关于 useCallback
的回答 这里。)
英文:
The problem is that because you don't have a type annotation on newList
or the new object in it, TypeScript infers the types of those properties from the types of the property values. When it does that, TypeScript infers ""
and "none"
to be of type string
(rather than the string literal types ""
and "none"
), but your interface requires the string literal types ""
and "none"
.
There are a couple of ways to fix it. Probably the most direct is to tell TypeScript that newList
is a listData[]
(but keep reading, there are a couple of other issues):
const addList = (item: any) => {
if (item !== "") {
const newList: listData[] = [ // <===========
...list,
{ text: item, isDone: false, viewMode: { display: "" }, editMode: { display: "none" } },
];
localStorage.setItem(
"listData",
JSON.stringify([
...list,
{
text: item,
isDone: false,
viewMode: { display: "" },
editMode: { display: "none" },
},
])
);
setList(newList);
}
};
Side note: The item
parameter's type should be string
, not any
:
const addList = (item: string) => {
// −−−−−−−−−−−−−−−−^^^^^^
By making it any
, you leave yourself open to doing addList(42)
, which will put an object in your list with text: 42
, which isn't what the type says it will be.
That said, a couple of other things:
- Whenever you're setting state based on existing state, ti's best to use the callback form of the state setter rather than relying on the copy of
list
thataddList
closes over, because that can be stale. - There's no reason to repeat the code adding the new item, just use
newList
when storing in local storage.
So:
const addList = (item: string) => {
if (item !== "") {
setList((list) => {
const newList: listData[] = [
...list,
{
text: item,
isDone: false,
viewMode: { display: "" },
editMode: { display: "none" },
},
];
localStorage.setItem("listData", JSON.stringify(newList));
return newList;
});
}
};
Using the callback form also lets you memoize addList
, which could be useful if the component you're passing it to is memoized. Giving that component a new addList
every time forces it to re-render, whereas if addList
is stable (because it's memoized), the component may be able to avoid re-rendering. But whether you want to memoize addList
depends on the component you're giving it to. (More about that in my answer about useCallback
here.)
答案2
得分: 0
const newItem: listData = { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }
const newList = [...list, newItem];
英文:
A fix for the first error with the type:
const newItem: listData = { text: item, isDone: false, viewMode: { display: '' }, editMode: { display: 'none' } }
const newList = [...list, newItem];
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论