To convert existing code over to TypeScript:
.js
to either .ts
(if there is no html or jsx in the file) or .tsx
(if there is).The first thing that will probably happen when you convert a .js
file in our system to .ts
is that some imports will be lacking types.
Temporarily get around the issue by adding the missing type in the typings/@elastic/eui/index.d.ts
file. Bonus points if you write a PR yourself to the EUI repo to add the types, but having them available back in OpenSearch Dashboards will take some time, as a new EUI release will need to be generated, then that new release pointed to in OpenSearch Dashboards. Best, to make forward progress, to do a temporary workaround.
// typings/@elastic/eui/index.d.ts
declare module '@elastic/eui' {
// Add your types here
export const EuiPopoverTitle: React.FC<EuiPopoverTitleProps>;
...
}
// you can now import it in <your-plugin-file.ts>
import { EuiPopoverTitle } from '@elastic/eui';
Some background on the differences between module declaration and augmentation:
In TypeScript module declarations can not be merged, which means each module can only be declared once. But it is possible to augment previously declared modules. The documentation about the distinction between module declaration and augmentation is sparse. The observed rules for declare module '...' {}
in a .d.ts
file seem to be:
A .d.ts
file is treated as a module if it contains any top-level import
or export
statements. That means that in order to write a module declaration the import
s must be contained within the declare
block and none must be located on the topmost level. Conversely, to write a module augmentation there must be at least one top-level import
or export
and the declare
block must not contain any import
statements.
Since @elastic/eui
already ships with a module declaration, any local additions must be performed using module augmentation, e.g.
// file `typings/@elastic/eui/index.d.ts`
import { CommonProps } from '@elastic/eui';
import { FC } from 'react';
declare module '@elastic/eui' {
export type EuiNewComponentProps = CommonProps & {
additionalProp: string;
};
export const EuiNewComponent: FC<EuiNewComponentProps>;
}
For example:
metadata.js:
export let metadata = null;
export function __newPlatformInit__(legacyMetadata) {
...
}
documentation_links.js:
import { metadata } from './metadata';
export const DOC_LINK_VERSION = metadata.branch;
To TypeScript documentation_links.js
you'll need to add a type definition for metadata.js
metadata.d.ts
declare interface Metadata {
public branch: string;
}
declare const metadata: Metadata;
export { metadata };
yarn add -D @types/markdown-it@8.4.1
Use the version number that we have installed in package.json. This may not always work, and you might get something like:
Please choose a version of "@types/markdown-it" from this list:
If that happens, just pick the closest one.
If yarn doesn't find the module it may not have types. For example, our rison_node
package doesn't have types. In this case you have a few options:
types
folder and point to that in the tsconfig.// @ts-ignore
line above the import. This should be used minimally, the above options are better. However, sometimes you have to resort to this method.React has it's own concept of runtime types via proptypes
. TypeScript gives you compile time types so I prefer those.
Before:
import PropTypes from 'prop-types';
export class Button extends Component {
state = {
buttonWasClicked = false
};
render() {
return <button onClick={() => setState({ buttonWasClicked: true })}>{this.props.text}</button>
}
}
Button.proptypes = {
text: PropTypes.string,
}
After:
interface Props {
text: string;
}
interface State {
buttonWasClicked: boolean;
}
export class Button extends Component<Props, State> {
state = {
buttonWasClicked = false
};
render() {
return <button onClick={() => setState({ buttonWasClicked: true })}>{this.props.text}</button>
}
}
Note that the name of Props
and State
doesn't matter, the order does. If you are exporting those interfaces to be used elsewhere, you probably should give them more fleshed out names, such as ButtonProps
and ButtonState
.
In react proptypes, we often will use PropTypes.func
. In TypeScript, a function is () => void
, or you can more fully flesh it out, for example:
(inputParamName: string) => string
(newLanguage: string) => void
() => Promise<string>
Especially since we often use the spread operator, this syntax is a little different and probably worth calling out.
Before:
function ({ title, description }) {
...
}
After:
function ({ title, description }: {title: string, description: string}) {
...
}
or, use an interface
interface Options {
title: string;
description: string;
}
function ({ title, description }: Options) {
...
}
any
as little as possibleUsing any is sometimes valid, but should rarely be used, even if to make quicker progress. Even Unknown
is better than using any
if you aren't sure of an input parameter.
If you use a variable that isn't initially defined, you should give it a type or it will be any
by default (and strangely this isn't a warning, even though I think it should be)
Before - color
will be type any
:
let color;
if (danger) {
color = 'red';
} else {
color = 'green';
}
After - color
will be type string
:
let color: string;
if (danger) {
color = 'red';
} else {
color = 'green';
}
Another quirk, default Map\WeakMap\Set
constructors use any-based type signature like Map<any, any>\WeakMap<any, any>\Set<any>
. That means that TS won't complain about the piece of code below:
const anyMap = new Map();
anyMap.set('1', 2);
anyMap.set('2', '3');
anyMap.set(3, '4');
const anySet = new Set();
anySet.add(1);
anySet.add('2');
So we should explicitly define types for default constructors whenever possible:
const typedMap = new Map<string, number>();
typedMap.set('1', 2);
typedMap.set('2', '3'); // TS error
typedMap.set(3, '4'); // TS error
const typedSet = new Set<number>();
typedSet.add(1);
typedSet.add('2'); // TS error
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )