打字稿中的简单 react-dnd 列表在尝试 运行 示例时给我编译错误

Simple react-dnd list in typescript is giving me compilation errors when trying to run the example

我正在尝试让这个漂亮的 react-dnd 示例正常工作,但我目前遇到了一些错误:

import * as React from 'react';
import {
  DragDropContext,
  Draggable,
  Droppable,
  DroppableProvided,
  DraggableLocation,
  DropResult,
  DroppableStateSnapshot, DraggableProvided, DraggableStateSnapshot
} from 'react-beautiful-dnd';
import {Flex} from 'grid-styled'
import './App.css';

interface Item {
  id: string;
  content: string;
}
interface IAppState {
  items: Item[];
  selected: Item[];
}
interface IMoveResult {
  droppable: Item[];
  droppable2: Item[];
}

const getItems = (count: number, offset:number = 0): Item[] => {
  return Array
    .from({length: count}, (v, k) => k)
    .map(k => ({
      content: `item ${k + offset}`,
      id: `item-${k + offset}`
    }));
};

const reorder = (list: Item[], startIndex: number, endIndex: number):Item[] => {
  const result = [...list];
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};


/**
 * Moves an item from one list to another list.
 */
const move = (source: Item[], destination: Item[], droppableSource:DraggableLocation, droppableDestination:DraggableLocation):IMoveResult | any => {
  const sourceClone = [...source];
  const destClone = [...destination];
  const [removed] = sourceClone.splice(droppableSource.index, 1);

  destClone.splice(droppableDestination.index, 0, removed);

  const result = {};
  result[droppableSource.droppableId] = sourceClone;
  result[droppableDestination.droppableId] = destClone;

  return result;
};

const grid:number = 8;

const getItemStyle = (draggableStyle: any, isDragging: boolean):{} => ({
  userSelect: 'none',
  padding: 2*grid,
  margin: `0 0 ${grid}px 0`,
  background: isDragging ? 'lightgreen' : 'grey',
  ...draggableStyle
});

const getListStyle = (isDraggingOver: boolean):{} => ({
  background: isDraggingOver ? 'lightblue' : 'lightgrey',
  padding: grid,
  width: 300,
  minHeight: 400
});

export default class App extends React.Component<{}, IAppState> {

  public id2List = {
    droppable: 'items',
    droppable2: 'selected'
  };

  constructor(props:any) {
    super(props);

    this.state = {
      items: getItems(10, 0),
      selected: getItems(5, 10)
    };

    this.onDragEnd = this.onDragEnd.bind(this);
    this.getList = this.getList.bind(this);
  }

  public getList (id:string):Item[] {
    return this.state[this.id2List[id]];
  }

  public onDragEnd(result: DropResult):void {

    const { source, destination } = result;

    if (!destination) {
      return;
    }

    if (source.droppableId === destination.droppableId) {
      const items = reorder(
        this.getList(source.droppableId),
        source.index,
        destination.index
      );

      let state:IAppState = {...this.state};

      if (source.droppableId === "droppable2") {
        state = { ...this.state, selected: items };
      } else if (source.droppableId === "droppable") {
        state = {...this.state, items}
      }

      this.setState(state);

    } else {
      const resultFromMove:IMoveResult = move(
        this.getList(source.droppableId),
        this.getList(destination.droppableId),
        source,
        destination
      );

      this.setState({
        items: resultFromMove.droppable,
        selected: resultFromMove.droppable2
      });
    }
  }

  public render() {
    return (
      <DragDropContext onDragEnd={this.onDragEnd}>
        <Flex justifyContent={"space-between"}>
          <Flex flexDirection="column">
            <Droppable droppableId="droppable">
              {(provided:DroppableProvided, snapshot:DroppableStateSnapshot) => (
                <div
                  ref={provided.innerRef}
                  {...provided.droppableProps}
                  style={getListStyle(snapshot.isDraggingOver)}
                >
                  {this.state.items.map((item, index) => (
                    <Draggable key={item.id} draggableId={item.id} index={index}>
                      {(providedDraggable:DraggableProvided, snapshotDraggable:DraggableStateSnapshot) => (
                          <div>
                            <div
                              ref={providedDraggable.innerRef}
                              {...providedDraggable.draggableProps}
                              {...providedDraggable.dragHandleProps}
                              style={getItemStyle(
                                providedDraggable.draggableProps.style,
                                snapshotDraggable.isDragging
                              )}
                            >
                              {item.content}
                            </div>
                            {providedDraggable.placeholder}
                          </div>
                        )}
                    </Draggable>
                  ))}
                  {provided.placeholder}
                </div>
              )}
            </Droppable>
          </Flex>
          <Droppable droppableId="droppable2">
            {(providedDroppable2:DroppableProvided, snapshotDroppable2:DroppableStateSnapshot) => (
              <div
                ref={providedDroppable2.innerRef}
                style={getListStyle(snapshotDroppable2.isDraggingOver)}>
                {this.state.selected.map((item, index) => (
                  <Draggable
                    key={item.id}
                    draggableId={item.id}
                    index={index}>
                    {(providedDraggable2:DraggableProvided, snapshotDraggable2:DraggableStateSnapshot) => (
                      <div>
                        <div
                          ref={providedDraggable2.innerRef}
                          {...providedDraggable2.draggableProps}
                          {...providedDraggable2.dragHandleProps}
                          style={getItemStyle(
                            providedDraggable2.draggableProps.style,
                            snapshotDraggable2.isDragging
                          )}>
                          {item.content}
                        </div>
                        {providedDraggable2.placeholder}
                      </div>
                    )}
                  </Draggable>
                ))}
                {providedDroppable2.placeholder}
              </div>
            )}
          </Droppable>
          </Flex>
      </DragDropContext>
    );
  }

}

参考:https://github.com/abeaudoin2013/react-beautiful-dnd-multi-list-typescript-example/blob/master/src/App.tsx

目前我在尝试 运行 这个例子时看到了 2 个错误:

错误#1 元素隐式具有 'any' 类型,因为类型 'string' 的表达式不能用于索引类型“{}”。 在类型“{}”上找不到参数类型为 'string' 的索引签名。 TS7053

result[droppableSource.droppableId] = sourceClone;

错误#2 元素隐式具有 'any' 类型,因为类型 'any' 的表达式不能用于索引类型 'Readonly'。 TS7053

public getList(id: string): 项目[] {

99 | return this.state[this.id2List[id]]; | ^ 100 | }

这是我的依赖项:

"dependencies": {
    "@reduxjs/toolkit": "^1.5.1",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "@types/jest": "^24.0.0",
    "@types/node": "^12.0.0",
    "@types/react": "^16.9.0",
    "@types/react-beautiful-dnd": "^13.1.2",
    "@types/react-dom": "^16.9.0",
    "@types/react-redux": "^7.1.7",
    "@types/react-router-dom": "^5.3.2",
    "react": "^17.0.2",
    "react-beautiful-dnd": "^13.1.0",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.0",
    "react-router-dom": "^6.0.2",
    "react-scripts": "4.0.3",
    "typescript": "~4.1.5"
  },

如果您使用 TypeScript,则必须为您的变量提供类型。否则类型检查根本没有帮助。

const result : { [key:string]:Item[]; } = {};

应该解决第一个问题。我没有尝试代码,如果它不起作用我会更正它。

这个构造也是如此:

 public id2List = {
    droppable: 'items',
    droppable2: 'selected'
  }; // <-- what is the type here?

我猜应该是

  public id2List = {
    droppable: 'items',
    droppable2: 'selected'
  } as  { [key:string]: keyof IAppState; };

要了解有关这些类型的更多信息,您可以查看 TypeScript 中的 Index Signatures 概念。此外,如果您可以避免它们并在类型中使用适当的属性名称,那么最好这样做,因为缩小类型可以使您的代码更不容易出错。

因为 App 组件是参数化的 class,它得到 IAppState 作为 this.state 的类型。要将 id2List 值绑定到 IAppState 中的 属性 名称,使用 keyof 运算符。

值得一提的是,如果您不能或不想找出正确的类型,您也可以将 'any' 明确声明为一种类型。 IE。 const result: any = {}; 可能仍然可以被编译器接受,具体取决于设置。但它也无视任何首先使用 TS 的理由。