Use TypeScript
has been a while, this is really a good thing, although the use of the process also found some bug
, but are small problems, so the overall experience is very good.
TypeScript
It Type
is called, and its strong type is inseparable, this is also different from the JavaScript
most critical point, the type of declaration can be directly written in the code, you can also write a separate description of the type of the file *.d.ts
.
Common ways
In the first place there d.ts
are no simple basic type definitions (since these are written in expressions, variables, and there is no meaning in the definition), and the declarations are often defined in the types of complex structures.
Most of the syntax is consistent with the syntax written in ts
the normal file and is export
followed by the member to be exported.
The simplest is to use type
keywords to define:
type A = { // 定义复杂结构 b: number c: string}type Func = () => number // 定义函数type Key = number | string // 多个类型
Combination type
And there's a TypeScript
very easy way to reuse it in type
, for example, we have a Animal
type, and a Dog
type that you can use &
to reuse.
P.s> &
symbols can be stitched together with multiple
type Animal = { weight: number height: number}type Dog = Animal & { leg: number}
Dynamic JSON type designation
If we have a JSON
structure, and it's key
dynamic, then we certainly can't key
write everything in the code, we just need to simply specify a wildcard character:
type info = { [k: string]: string | number // 可以指定多个类型}const infos: info = { a: 1, b: '2', c: true, // error 类型不匹配}
It is also recommended to use built-in functions for the new version Record
:
const infos: Record<string, string | number> = { a: 1, b: '2', c: true, // error}
Gets the type of the variable
If we have a JSON object that contains name
age
two properties, we can TypeScript
implement some interesting things through some built-in tool functions.
keyof
with the typeof
combination we can get the results we want:
const obj = { name: 'Niko', age: 18}// 如果是这样的取值,只能写在代码中,不能写在 d.ts 文件中,因为声明文件里边不能存在实际有效的代码type keys = keyof typeof objlet a: keys = 'name' // passlet b: keys = 'age' // passlet c: keys = 'test' // error
JSON
JSON
This can also be used if we want to modify a type that is not uniform to a uniform type:
const obj = { name: 'Niko', age: 18, birthday: new Date()}const infos: Record<keyof typeof obj, string> = { name: '', age: '', birthday: 123, // 出错,提示类型不匹配 test: '', // 提示不是`info`的已知类型}
Gets the return value type of the function
For example, if we have a function, the function returns one JSON
, and we need this to be the JSON
type.
Then it can be ReturnType<>
achieved by:
function func () { return { name: 'Niko', age: 18 }}type results = ReturnType<typeof func>// 或者也可以拼接 keyof 获取所有的 keytype resultKeys = keyof ReturnType<typeof func>// 亦或者可以放在`Object`中作为动态的`key`存在type infoJson = Record<keyof ReturnType<typeof func>, string>
Declaring a function in code and
class
Type
Because we know that functions and class
when they are created have actual code (function body, constructor).
But we are writing in the d.ts
declaration file, this is only a constraint on the type, so there must be no real code, but if it is written in the ordinary ts
file will be wrong, so in order to this kind of situation, we need to use the declare
keyword, It means that we are here to define a type, not an object, a function:
class Personal { name: string // ^ 出错了,提示`name`必须显式的进行初始化}function getName (personal: Personal): name// ^ 出错了,提示函数缺失实现
The following are the correct ways to use:
-declare class Personal {+declare class Personal { name: string}-function getName (personal: Personal): name+declare function getName (personal: Personal): name
Of course, in general, it is not recommended that class
the definition, should be used interface
instead of it, this class
should only exist in the description of non- TS
modules, if it is a module developed by itself, then the structure of itself has the characteristics of the declaration type.
function overloading
This concept is in some strongly-typed languages, depending on TypeScript
, this is also a strong type of language, so there will be a need to use this kind of declaration place.
For example, we have a add
function that can be used to string
splice the parameters of the type, or to number
add the parameters of the receiving type.
It is important to note that the definition of functions can be placed in a file only when the function overload definition of a third-party plug-in is made, and the implementation of the d.ts
function is recommended in other contexts (although the configuration paths
can be handled separately, it loses the constraint on the creation of the function)
// index.ts// 上边是声明function add (arg1: string, arg2: string): stringfunction add (arg1: number, arg2: number): number// 因为我们在下边有具体函数的实现,所以这里并不需要添加 declare 关键字// 下边是实现function add (arg1: string | number, arg2: string | number) { // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2 if (typeof arg1 === 'string' && typeof arg2 === 'string') { return arg1 + arg2 } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { return arg1 + arg2 }}
TypeScript
Function overloading is also just the declaration of multiple functions, the specific logic also need to write their own, he will not really have your multiple functions of the same function of the merging of the body
Order problems for multiple functions
Imagine if we had a function that passed the parameter of the Date
type, returned its timestamp unix
, if passed in Object
, then output the object's specific type, and the toString
rest would return directly, what should a function like this be written?
Just a sample demonstration, normal people will not write such a function ...
function build (arg: any) { if (arg instanceof Date) { return arg.valueOf() } else if (typeof arg === 'object') { return Object.prototype.toString.call(arg) } else { return arg }}
But such a function overload in the order of the Declaration is very fastidious, it is important to put the accuracy of high in front:
// 这样是一个错误的示例,因为无论怎样调用,返回值都会是`any`类型function build(arg: any): anyfunction build(arg: Object): stringfunction build(arg: Date): number
Because TypeScript
after finding a declaration of a function overload, it stops and does not continue to find, any
is a most ambiguous range, and is Object
contained Date
, so we should arrange from small to large in order:
function build(arg: Date): numberfunction build(arg: Object): stringfunction build(arg: any): any// 这样在使用的时候才能得到正确的类型提示const res1 = build(new Date()) // numberconst res2 = build(() => { }) // stringconst res3 = build(true) // any
Some scenarios that do not require function overloading
The meaning of function overloading is to let you know that passing in different parameters results in a different result, but if the arguments passed in are different, but the resulting ( type ) is the same, then you should not use function overloading (meaningless).
If the function has the same return value type, then you do not need to use function overloading
function func (a: number): numberfunction func (a: number, b: number): number// 像这样的是参数个数的区别,我们可以使用可选参数来代替函数重载的定义function func (a: number, b?: number): number// 注意第二个参数在类型前边多了一个`?`// 亦或是一些参数类型的区别导致的function func (a: number): numberfunction func (a: string): number// 这时我们应该使用联合类型来代替函数重载function func (a: number | string): number
Interface
interface
Is in the TypeScript
middle of the exclusive, in JavaScript
and not interface
a say.
Because it interface
is only used to specify the corresponding behavior of implementing it class
, there is no real code, which is an invalid operation for the scripting language.
There is not much difference in syntax, class
but only in the declaration of interface
member properties, such as the function
ability to write specific received parameters and the type of return value, can not be written in the interface
specific function body, the same, for member properties can not directly in the interface
to assign values in:
// 这是一个错误的示例interface PersonalIntl { name: string = 'Niko' sayHi (): string { return this.name }}// 在 interface 中只能存在类型声明interface PersonalIntl { name: string sayHi (): string}
In some cases interface
, there is no difference between the use and the normal type
definition.
For example, we want to export an name
object that exists and has age
two attributes:
// types/personal.d.tsexport interface PersonalIntl { name: string age: number}// index.d.tsimport { PersonalIntl } from './types/personal'const personal: PersonalIntl = { name: 'Niko', age: 18,}
If you are going to interface
change type
the definition, it is perfectly fine:
// types/personal.d.tsexport type PersonalIntl = { name: string age: number}
Such a definition is completely no problem based on the use of the top, but it is only applicable to the Object
literal declaration, there is no way to use a good constraint class
mode, so we use interface
to constrain class
the implementation:
import { PersonalIntl } from './types/personal'class Personal implements PersonalIntl { constructor(public name: string, public age: number) { } // 上边的简写与下述代码效果一致 public name: string public age: number constructor (name: string, age: number) { this.name = name this.age = age }}const personal = new Personal('niko', 18)
Some doubts about the function member declaration
First, there are two ways to define a function in an interface, one is defined on the instance and one is defined on the prototype chain.
The two ways of declaring this are as follows:
interface PersonalIntl { func1 (): any // 实例属性 func2: () => any // 原型链属性}
But when we implement these two properties we can actually convert to each other, and there is no strong requirement to use which way:
class Personal implements PersonalIntl { func1 () { console.log(this) } func2 = () => { console.log(this) }}
In fact, the two are different in the compiled JavaScript
code, it is not clear whether this is a design is the bug
case, similar to the structure:
var Personal = /** @class */ (function () { function Personal() { var _this = this; this.func2 = function () { console.log(_this); }; } Personal.prototype.func1 = function () { console.log(this); }; return Personal;}());
Therefore, it is advisable interface
to create in a defined way, avoiding some strange and bizarre problems that may exist.
Automatic merging of interface declarations
Because interface
it is TypeScript
unique, there will be some interesting features, such as the same name interface
will be automatically merged:
interface PersonalIntl { name: string}interface PersonalIntl { age: number}class Personal implements PersonalIntl { name = 'Niko' age = 18}
Do not use function overloading in interface
interface
using a function overload in, you get an incorrect result, or the upper build
function, if interface
declared in, then implemented in, class
then whatever the call, the type of the return value will be considered any
.
So the correct approach is to class
declare the overloads in, class
implemented in, with interface
a maximum of only one definition any
, not three overloads.
class Util implements UtilIntl { build(arg: Date): number build(arg: Object): string build(arg: any): any build(arg: any) { if (arg instanceof Date) { return arg.valueOf() } else if (typeof arg === 'object') { return Object.prototype.toString.call(arg) } else { return arg } }}
Summary
TypeScript
related to declaring type declarations are currently summarized in these more commonly used, welcome to the small partners to complement.
There is a definition of existence and in the previous version module
namespace
, but for the time being, it seems to be more recommended to use the Es-modules version import
/ export
to implement a similar function rather than a custom syntax, so we skipped over the two keyword-related descriptions
There are templates in the official documentation for how to write a declaration file, which can be referenced in: Transfer matrix
Resources
- Keyof
- Record
- ReturnType and other built-in functions
How to write a Typescript declaration file