跳到主要内容
版本:0.17.0+

ts howto

  1. react 声明 state(ReadOnly)属性
  2. TODO: research on .d.ts
  3. BEST-PRACTICE: how to define const array with types
    1. webstorm typescript live template
    2. sample source code
    3. a little reflection
  4. htmlFor in label
  5. private property in interface
  6. [Give Up] typeorm find error types
  7. instanceof --> istypeof ?
  8. PASS: declare const array in d.ts
  9. hwo to define useImperativeHandler interface
    1. ReturnType of a function
  10. how to design interface with default value
  11. what's TS_NODE_TRANSPILE_ONLY
  12. TODO: cast enum to string
  13. how to import .md like resources
  14. how to check object's specific type when defined as a union type

react 声明 state(ReadOnly)属性

Props,再State

interface State {
bar: number;
}

interface Props {
baz: number;
}

class Foo extends React.Component<Props, State> {
public state: State = {
bar: 5,
};
}

ref:

TODO: research on .d.ts

BEST-PRACTICE: how to define const array with types

webstorm typescript live template

export const $ConstVAR$ = '$VAR$';
export type $VAR$ = typeof $ConstVAR$;

$END$;

sample source code

// sample of one unit
export const errorInvalidSuffix = 'ErrorInvalidSuffix';
export type ErrorInvalidSuffix = typeof errorInvalidSuffix;

export const errorUnknownEncoding = 'ErrorUnknownEncoding';
export type ErrorUnknownEncoding = typeof errorUnknownEncoding;

export const errorPreParsingRows = [
errorInvalidSuffix, // 后缀
errorUnknownEncoding, // 编码
] as const;
export type ErrorPreParsingRows = typeof errorPreParsingRows[number];

// sample of joining units
export const errorParsingFiles = [
...errorPreParsingRows, // 解析行之前的问题
...errorParsingRows, // 解析行时的问题
] as const;
export type ErrorParsingFile = typeof errorParsingFiles[number];

a little reflection

After a little try of this camel case const varied types, I found it a bit inflexible.

So I finally turns into the usage of simple const === type, especially support for the use of redux actions.

htmlFor in label

In materialUI, they used a custom element based on input with a label.

// https://mui.com/zh/components/buttons/#upload-button
const Input = styled('input')({
display: 'none',
});

export default function UploadButtons() {
return (
<Stack direction="row" alignItems="center" spacing={2}>
<label htmlFor="contained-button-file">
<Input
accept="image/*"
id="contained-button-file"
multiple
type="file"
/>
<Button variant="contained" component="span">
Upload
</Button>
</label>
...
</Stack>
);
}

In this demo, the label has a property of htmlFor, which points to an element with id of contained-button-file, i.e the element of Input which accepts images.

However, this may cause the eslint problem since in eslint the label should points to an built-in element.

There are 2 solutions.

  1. add the eslint configuration:

    refer to here: Case: My label and input components are custom components., we can add the configuration of custom elements as the following:

    // .eslintrc
    {
    "rules": {
    "jsx-a11y/label-has-associated-control": [
    2,
    {
    "labelComponents": ["CustomInputLabel"],
    "labelAttributes": ["label"],
    "controlComponents": ["CustomInput"],
    "depth": 3
    }
    ]
    }
    }
  2. give up the custom elements if unnecessary

    In our demo, since MaterialUI just does one little work on the built-in element of input, i.e. adds the display: 'none' style onto the element.

    So if we don't have the requirement of re-using this element, we can avoid using custom elements, and re-write the codes like the following:

    export default function UploadButtons() {
    return (
    <Stack direction="row" alignItems="center" spacing={2}>
    <label htmlFor="contained-button-file">
    <input
    style={{display: 'none'}}
    accept="image/*"
    id="contained-button-file"
    multiple
    type="file"
    />
    <Button variant="contained" component="span">
    Upload
    </Button>
    </label>
    ...
    </Stack>
    );
    }

private property in interface

I intended to make the property from interface into implemented class private, so that I can use methods to export those properties, but to find it's impossible.

// src/main/modules/parseFile/handler/parse_base.ts:16
export interface IParseResult {
startTime: Date;
parseEndTime: Date;
dbEndTime: Date;
parseMileSeconds: number;
dbMileSeconds: number;
nTotalRows: number;
nSavedRows: number;
nFailedValidation: number;
sizePct: number;
rowsPct: number;
}

And actually what I want to do is because I didn't catch really what an interface means, and how to design a robust interface with class.

The IParseResult is really what I want, that means, the exported data-structure.

However, I cannot use a class to implement directly on this interface, so that I operate the public methods inner the class to alter these properties.

The proper way is to define a class to store all or partial data properties of this interface, and do operations, and then, finally export those values.

If I do not want to export each time, since I want to use a variable like parseResult directly in the scope, then I can design a class with accepts the parseResult then alter its value by methods so that the changes happen in place.

In conclusion, we can define an interface, and the interface is for outer usage, rather than implemented by a class, which, if so, needs to be implemented like this:

interface IPipeParseResult {
...

export: () => IParseResult
}

class ParseResult implements IPipeParseResult {
...

public expo
rt(): IParseResult {
return {
...
}
}
}

Not like this, which has no meaning:

class ParseResult implements IParseResult {
....
}

So, to my former project, I gave it up, since no need to be that sophisticated then.

ref:

[Give Up] typeorm find error types

I intended to use error type to check the database execution result.

picture 37

However, I failed, since the specific error type is not provided by the typeorm instead of from third party library like sqlite3-driver or so.

The query result module is written at typeorm/QueryBuilder.ts at beea2e1e4429d13d7864ebc23aa6e58fa01647ea · typeorm/typeorm, in which it just merges the third-party query result with typeorm's inner query result defined at typeorm/QueryFailedError.ts at beea2e1e4429d13d7864ebc23aa6e58fa01647ea · typeorm/typeorm.

Hence, unless we spent a lot of time to figure out what the lib is using and where the types of the lib defines, (and possibly we could find nothing since they may be written via C/C++).

So just let it go, and do more harding coding then. Those don't deserve.

instanceof --> istypeof ?

No! In typescript, there is no api like instanceof to type check.

However, we can define a method which returns a is A to determine.

ref:

PASS: declare const array in d.ts

picture 5

temporary solution: use enum instead of const array picture 6

ref:

hwo to define useImperativeHandler interface

It's quite clarified in this answer that when I define a useImperativeHandler, I need to make at least four steps.

picture 76

First, I should let the useRef know not only the component type (which initialized as null) but also the Handler type, in which defined the api interface, so that I can use ref.current.doSomething() and share the full auto-complement from IDE.

Second, how to define the Handler type? The easier way is derive it directly from the component using typeof ForwardRefType.

However, if we hadn't defined what's the Handler we are to use in our ForwardRefType, then those work still wouldn't work. It forced us to clearly define the Handler the component is using.

Nevertheless, I am not willing to write an interface which is totally equal to the type of function. So I struggled to find a ReturnType to help me auto complete this matter.

And I found a way to write like this: picture 77

First, I peeled off(剥离) the imperativeHandler api separately, and the cost is to use a function since the Handler depends the ref.

Then I use this Handler again, to let its ReturnType serve as the generic type of forwardRef (this is the most important!).

Finally, I use the type of forwardRef as the generic type of its father ref element, and then it would know what api to use. All things are done! picture 78

There is a last problem about this approach, that is why I cannot make the ref init value work even with null as unknown as ScrollToBottom, which forced me to write like ref?.current?.doScroll() rather than ref.current.doScroll().

But it's not so important yet now. Maybe I would turn back to solve this in the future need.

[TODO: why null as unknown as ScrollToBottom not work]

ref:

ReturnType of a function

function f1(s: string) {
return {a: 1, b: s};
}
type T14 = ReturnType<typeof f1>; // { a: number, b: string }

ref:

how to design interface with default value

There has been quite a little of insightful solutions offered in this question Typescript interface default values - Stack Overflow.

And I finally choose the factory function way since it's more straight-forward and can meet my need also.

Then my interface is designed into like this:

import {MsgFromMain, MsgLevel} from '../universal';

export interface ConsoleItem {
text: string;
time: Date;
level: MsgLevel;
}

export const makeItemFromMain = (
msg: MsgFromMain,
func?: (any) => string,
): ConsoleItem => ({
text: func ? func(msg.content) : msg.content,
time: msg.sendTime,
level: msg.level || MsgLevel.debug,
});

export const makeItemFromText = (
text: string,
level = MsgLevel.debug,
): ConsoleItem => ({
text,
time: new Date(),
level,
});

In this interface, any of text | time | level can get a default value either from another input or so, and it frees me from redundant coding and allows me to write like this:

picture 75

Thank you!

ref:

what's TS_NODE_TRANSPILE_ONLY

In a word, transpile_only is used to fasten the transition speed from ts into js.

picture 61

ref:

TODO: cast enum to string

If I define a EnumType of enum MenuKey {ERP="ERP", TRD="TRD"} since I need a data structure to limit a variable domain.

Obviously in my case, they are all string, which means the real intension of me is something like enum MenuKey {xxx} as string so that I can put my MenuKey into an API which only accepts string, e.g: slice.

Hence, I can write like the following since ts would treat mk as a string so that has an api of slice:

const mk: MenuKey = 'ERP';
console.log(mk.slice(0, 2));

However, it doesn't.

The only way I can do is write like this:

console.log((mk as string).slice(0, 2));

Please read another example from the real business scene:

enum MenuKey {
ERP = 'ERP',
TRD = 'TRD',
}

// first realization, definitely not good since we need to write `as string` each time using it as a string
const [menuKey, setMenuKey] = useState(MenuKey.ERP);
console.log((menuKey as string).slice(0, 2)); // not good

// second realization, a little better
const [menuKey, setMenuKey] = useState(MenuKey.ERP as string);
console.log(menuKey.slice(0, 2));

I wonder if there is a built-in practice that allows me done this right.

Thanks.

how to import .md like resources

Just declare it as a module.

// globals.d.ts
declare module '*.md';
picture 2

ref:

how to check object's specific type when defined as a union type

This problem may seem easy is many programming languages, e.g, you can use isinstane(x, X) in python.

However, it becomes tricky when things in TypeScript since so many that are close to complex concepts can be really hard for a newbie to find the right way.

I tried to scratch some useful info from the official TypeScript website but to find neither union nor merge can satisfy my requirements. The combination may be a little helpful if we do some hook on this one.

On StackOverFlow, a relative post javascript - Is it possible to combine members of multiple types in a TypeScript annotation? - Stack Overflow caught my eyesight but indeed almost none of the answers can meet my expectation -- an built-in and elegant(优雅的) solution rather than write redundant and vulnerable(脆弱的) functions.

Finally, I found a wonderful typescript handbook named as typescript-cheatsheet at typescript-cheatsheet | A set of TypeScript related notes used for quick reference. The cheatsheet contains references to types, classes, decorators, and many other TypeScript related subjects. , where it's easy for me to locate into the target chapter: typescript-cheatsheet | A set of TypeScript related notes used for quick reference. The cheatsheet contains references to types, classes, decorators, and many other TypeScript related subjects.

In this cheatsheet, it concludes two methods that can help me check an object's type, one by using type-guards which is best suitable for my current need, and the other one is by using discriminators which may be useful in the later projects.

If you are interested at discriminators, please move a step to Typescript discriminator. Very often we have use-cases where we… | by Dixit Patel | Medium for a better understanding since I am confused it with the javascript properties and typescript decorators.

Thanks to typescript-cheatsheet, it really helped me a lot and I can foresee that I will frequently seek for any typescript help from here rather than first searching on the stackoverflow.