有我的界面创建和分配有什么问题吗?

huangapple go评论82阅读模式
英文:

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: &#39;&#39; },
  editMode?: { display: &#39;none&#39; },
}

const [list, setList] = useState&lt;listData[]&gt;([]);

const addList = (item: any) =&gt; {
  if (item !== &quot;&quot;) {
    const newList = [...list, { text: item, isDone: false, viewMode: { display: &#39;&#39; }, editMode: { display: &#39;none&#39; } }];
    localStorage.setItem(&#39;listData&#39;, JSON.stringify([...list, { text: item, isDone: false, viewMode: { display: &#39;&#39; }, editMode: { display: &#39;none&#39; } }]));
    setList(newList);
  };
}

I got the error on vscode down below when I setList(newList):

Argument of type &#39;(listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; })[]&#39; is not assignable to parameter of type &#39;SetStateAction&lt;listData[]&gt;&#39;.
  Type &#39;(listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; })[]&#39; is not assignable to type &#39;listData[]&#39;.
    Type &#39;listData | { text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }&#39; is not assignable to type &#39;listData&#39;.
      Type &#39;{ text: string; isDone: boolean; viewMode: { display: string; }; editMode: { display: string; }; }&#39; is not assignable to type &#39;listData&#39;.
        The types of &#39;viewMode.display&#39; are incompatible between these types.
          Type &#39;string&#39; is not assignable to type &#39;&quot;&quot;&#39;.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 (
  &lt;&gt;
    &lt;div className={styles.bodyOuterDiv} &gt;
      ...
        &lt;ul &gt;
          {list.map((item, index) =&gt; {
            return (
              &lt;li key={index} className={styles.todoListOuterLi} &gt;
                &lt;div className={item.isDone ? styles.todoListItemsWithIsDone : styles.todoListItems} style={item.viewMode}&gt;
                        {item.text}
                &lt;/div&gt;
                &lt;textarea className={styles.todoListTextArea} value={item.text} onChange={(e) =&gt; { setItem(e.target.value) }} style={item.editMode} /&gt;
                      ...
               &lt;/li&gt;
             );
          })}
        &lt;/ul&gt;
       ...
     &lt;/div&gt;
  &lt;/&gt;
);

答案1

得分: 1

问题在于,因为您没有在 newList 或其中的新对象上添加类型注释,TypeScript 推断 这些属性的类型,将它们推断为 string 类型(而不是字符串文字类型 &quot;&quot;&quot;none&quot;),但您的接口需要字符串文字类型 &quot;&quot;&quot;none&quot;

修复的方法有几种。可能最直接的是告诉 TypeScript newListlistData[] 类型(但继续阅读,还有一些其他问题):

const addList = (item: any) => {
    if (item !== &quot;&quot;) {
        const newList: listData[] = [ // <===========
            ...list,
            { text: item, isDone: false, viewMode: { display: &quot;&quot; }, editMode: { display: &quot;none&quot; } },
        ];
        localStorage.setItem(
            &quot;listData&quot;,
            JSON.stringify([
                ...list,
                {
                    text: item,
                    isDone: false,
                    viewMode: { display: &quot;&quot; },
                    editMode: { display: &quot;none&quot; },
                },
            ])
        );
        setList(newList);
    }
};

Playground 链接


顺便说一下,item 参数的类型应该是 string,而不是 any

const addList = (item: string) => {
    // −−−−−−−−−−−−−−−−^^^^^^

将其设置为 any 会使您能够执行 addList(42),这将在列表中放置一个具有 text: 42 的对象,这不是类型所说的内容。


另外,还有一些其他事情:

  1. 每当基于现有状态设置状态时,最好使用状态设置器的回调形式,而不是依赖于 addList 闭合的 list 的副本,因为它可能已经过时。
  2. 没有必要重复添加新项的代码,只需在本地存储时使用 newList

所以:

const addList = (item: string) => {
    if (item !== &quot;&quot;) {
        setList((list) => {
            const newList: listData[] = [
                ...list,
                {
                    text: item,
                    isDone: false,
                    viewMode: { display: &quot;&quot; },
                    editMode: { display: &quot;none&quot; },
                },
            ];
            localStorage.setItem(&quot;listData&quot;, JSON.stringify(newList));
            return newList;
        });
    }
};

Playground 链接

使用回调形式还可以让您对 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 &quot;&quot; and &quot;none&quot; to be of type string (rather than the string literal types &quot;&quot; and &quot;none&quot;), but your interface requires the string literal types &quot;&quot; and &quot;none&quot;.

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) =&gt; {
    if (item !== &quot;&quot;) {
        const newList: listData[] = [ // &lt;===========
            ...list,
            { text: item, isDone: false, viewMode: { display: &quot;&quot; }, editMode: { display: &quot;none&quot; } },
        ];
        localStorage.setItem(
            &quot;listData&quot;,
            JSON.stringify([
                ...list,
                {
                    text: item,
                    isDone: false,
                    viewMode: { display: &quot;&quot; },
                    editMode: { display: &quot;none&quot; },
                },
            ])
        );
        setList(newList);
    }
};

Playground link


Side note: The item parameter's type should be string, not any:

const addList = (item: string) =&gt; {
    // −−−−−−−−−−−−−−−−^^^^^^

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:

  1. 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 that addList closes over, because that can be stale.
  2. 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) =&gt; {
    if (item !== &quot;&quot;) {
        setList((list) =&gt; {
            const newList: listData[] = [
                ...list,
                {
                    text: item,
                    isDone: false,
                    viewMode: { display: &quot;&quot; },
                    editMode: { display: &quot;none&quot; },
                },
            ];
            localStorage.setItem(&quot;listData&quot;, JSON.stringify(newList));
            return newList;
        });
    }
};

Playground link

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: &#39;&#39; }, editMode: { display: &#39;none&#39; } }
  const newList = [...list, newItem];

huangapple
  • 本文由 发表于 2023年5月25日 16:58:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76330518.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定