function keyCommandTransposeCharacters(editorState: EditorState): EditorState {
  const selection = editorState.getSelection();
  if (!selection.isCollapsed()) {
    return editorState;
  }

  const offset = selection.getAnchorOffset();
  if (offset === 0) {
    return editorState;
  }

  const blockKey = selection.getAnchorKey();
  const content = editorState.getCurrentContent();
  const block = content.getBlockForKey(blockKey);
  const length = block.getLength();

  // Nothing to transpose if there aren't two characters.
  if (length <= 1) {
    return editorState;
  }

  let removalRange;
  let finalSelection;

  if (offset === length) {
    // The cursor is at the end of the block. Swap the last two characters.
    removalRange = selection.set('anchorOffset', offset - 1);
    finalSelection = selection;
  } else {
    removalRange = selection.set('focusOffset', offset + 1);
    finalSelection = removalRange.set('anchorOffset', offset + 1);
  }

  // Extract the character to move as a fragment. This preserves its
  // styling and entity, if any.
  const movedFragment = getContentStateFragment(content, removalRange);
  const afterRemoval = DraftModifier.removeRange(
    content,
    removalRange,
    'backward',
  );

  // After the removal, the insertion target is one character back.
  const selectionAfter = afterRemoval.getSelectionAfter();
  const targetOffset = selectionAfter.getAnchorOffset() - 1;
  const targetRange = selectionAfter.merge({
    anchorOffset: targetOffset,
    focusOffset: targetOffset,
  });

  const afterInsert = DraftModifier.replaceWithFragment(
    afterRemoval,
    targetRange,
    movedFragment,
  );

  const newEditorState = EditorState.push(
    editorState,
    afterInsert,
    'insert-fragment',
  );

  return EditorState.acceptSelection(newEditorState, finalSelection);
}
Transpose the characters on either side of a collapsed cursor, or
if the cursor is at the end of the block, transpose the last two
characters.