当前位置:网站首页>Typescript learning [6] interface type
Typescript learning [6] interface type
2022-06-09 04:46:00 【Large American without sugar】
Interface Interface type
TypeScript It can not only help the front-end change the way of thinking , It can also strengthen the thinking and ability of interface oriented programming , And this is due to Interface Interface type . Through the interface type , We can clearly define what is inside the module 、 Cross module 、 Communication rules across project codes .
TypeScript The type detection of objects follows a process called “ The duck type ”(duck typing) perhaps “ Structured type (structural subtyping)” Principle of , That is, as long as the structures of the two objects are consistent , The types of properties and methods are consistent , Then their types are consistent .
The following is a sample code to first understand the interface type , As shown in the following code :
function Study(language: {
name: string; age: () => number }) {
console.log(` This is a paragraph created in ${
language.age()} Code years ago , Name is ${
language.name}`);
}
Study({
name: 'TypeScript',
age: () => new Date().getFullYear() - 2012 // This is a paragraph created in 10 Code years ago , Name is TypeScript
});
In the above code , We have defined a possession string Type attribute name、 Function type properties age The object of language As a function of parameters . meanwhile , We also use similar definitions JavaScript The syntax of object literals defines an inline interface type to constrain the type of parameter object .
then , We passed on a name The attribute is ‘TypeScript’ String 、age Property to call the function with the literal value of the object that calculates the year difference function as a parameter .
In the process of calling the function ,TypeScript Static type detected that the passed object literal type is string Of name The attribute and type are () => number Of age Property is consistent with the type defined by the function parameter , So you won't throw a type error .
If we pass in a name The attribute is number Type or missing age The object literal of the property , As shown in the following code :
Study({
name: 2; // error TS2322: Type 'number' is not assignable to type 'string'.
});
Study({
name: 'TypeScript'; // error TS2345: Argument of type '{ name: string; }' is not assignable to parameter of type '{ name: string; age: () => number; }'.Property 'age' is missing in type '{ name: string; }' but required in type '{ name: string; age: () => number; }'.
});
At this time , The first 2 The guild prompts an error : ts(2322) number Cannot assign a value to string, The first 7 Line will also prompt an error : ts(2345) Argument and formal parameter types are incompatible , Missing required attribute age.
Again , If we pass in an object literal that contains attributes that are not in the parameter type definition as arguments , You will also get a type error ts(2345), Argument and formal parameter types are incompatible , As shown in the following code :
Study({
id: 1, // error TS2345: Argument of type '{ id: number; name: string; age: () => number; }' is not assignable to parameter of type '{ name: string; age: () => number; }'.Object literal may only specify known properties, and 'id' does not exist in type '{ name: string; age: () => number; }'.
name: 'TypeScript',
age: () => new Date().getFullYear() - 2012
});
What's interesting is that , In the example above , If we first assign this object literal to a variable , Then pass the variable to the function to call , that TypeScript Static type detection will only detect the attribute types defined in the parameter type , And inclusively ignore any superfluous attributes , It will not throw a ts(2345) Type error . As shown in the figure below :
let ts = {
id: 2,
name: 'TypeScript',
age: () => new Date().getFullYear() - 2012
};
Study(ts); // ok
This is not an oversight or bug, Instead, it deliberately distinguishes between object literals and variables , We call this the case of object literals freshness.
Because this inline interface type is defined at the syntax level with the well-known JavaScript Deconstruction is quite similar , So it's easy to confuse us . Let's compare the effect of mixing deconstruction syntax with inline interface type through the following example .
// pure JavaScript Deconstructive grammar
function StudyJavaScript({
name, age}) {
console.log(name, age);
}
// TypeScript Mix inner deconstruction with inline type
function StudyTypeScript({
name, age}: {
name: string, age: () => number}) {
console.log(name, age);
}
/** pure JavaScript Deconstructive grammar , Define an alias */
function StudyJavaScript({
name: aliasName}) {
// Definition name Another name for
console.log(aliasName);
}
/** TypeScript */
function StudyTypeScript(language: {
name: string}) {
// console.log(name); // Can't print directly name
console.log(language.name);
}
From the above code we can see , In the function , Object deconstruction is similar to the syntax for defining interface types , Be careful not to confuse . actually , Defining inline interface types is not reusable , So we should use more interface Keyword to extract reusable interface types .
stay TypeScript in , The syntax of the interface is not very different from other types of languages , Let's see how the interface is defined by the code shown below :
interface test {
name: string;
age: () => number;
}
In the above code , We defined an interface , It contains an attribute of character type name And an attribute of a function type age . From this, we find that the syntax format of the interface is in interface After the space of the keyword + Interface name , Then, the definitions of attributes and attribute types are wrapped in flower brackets .
In the previous example , Defined by inline parameter types Study Function can be used directly test Interface to define parameters language The type of .
function Study(language: ProgramLanguage) {
console.log(` This is a paragraph created in ${
language.age()} Code years ago , Name is ${
language.name}`);
}
We can also constrain other logic by reusing interface type definitions . such as , We define a type as test The variable of TypeScript.
let TypeScript: test;
next , We assign an object literal that meets the interface type convention to this variable , As shown in the following code , At this time, there will be no prompt for type error .
TypeScript = {
name: 'TypeScript',
age: () => new Date().getFullYear() - 2012
}
And any non-compliance , Will prompt the wrong type . For example, we enter an empty object literal through the code shown below , An object literal type is also prompted {} The lack of name and age Attribute ts(2739) error .
TypeScript = {
}; // error TS2739: Type '{}' is missing the following properties from type 'test': name, age
Add... As shown in the code below name After attribute , An object literal type will still be prompted { name: string; } Missing required age Attribute ts( 2741) error .
TypeScript = {
name: 'TypeScript'
} // error TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'test'.
Besides , As shown in the following code , If we put one name The attribute is 2、age The attribute is ‘Jae Wong’ Is assigned to TypeScript , It will give you an error :ts(2322) number Type cannot be assigned to string And errors :ts(2322)string Cannot assign to function type .
TypeScript = {
name: 2, // error TS2322: Type 'number' is not assignable to type 'string'.
age: 'Jae Wong' // error TS2322: Type 'string' is not assignable to type '() => number'.
}
Or, as in the following example, there is an additional attribute that is not defined by the interface id, It will also prompt a ts(2322) error : Object literal cannot be assigned to test Variable of type TypeScript.
TypeScript = {
name: 'TypeScript',
age: () => new Date().getFullYear() - 2012,
id: 1 // error TS2322: Type '{ name: string; age: () => number; id: number; }' is not assignable to type 'test'.Object literal may only specify known properties, and 'id' does not exist in type 'test'.
}
Default attribute
In the previous example , If we want to lack age The object literal of the property can also meet the Convention without throwing type errors , Specifically, in interface types age Property can be defaulted , Then we can add the following after the attribute name ? Syntax to label default properties or methods . As in the following example OptionalTest Interface has a function type that can be defaulted age attribute .
interface test {
name: string;
age?: () => number;
}
let TypeScript: test;
TypeScript = {
name: 'TypeScript'
}
When the attribute is marked as defaultable , Its type becomes an explicitly specified type and undefined Of types Joint type , For example, in the example test Of age The attribute type becomes as follows :
(() => number) | undefined;
Think about it , Below test2 And test Is it equivalent :
interface test2 {
name: string;
age: (() => number) | undefined;
}
The answer, of course, is no equivalence , It can be defaulted signify You may not set the attribute key name , The type is undefined signify The attribute key name cannot be defaulted .
Since the value may be undefined , If we need to operate on the properties or methods of the object , You can use Type guard or Add... After the attribute name ? , As shown in the following code :
if (typeof test.age === 'function') {
test.age();
}
test.age?.()
adopt typeof conditional , After making sure age We only call when the property is a function , This avoids runtime prompts age Not a function error .
Read-only property
We may encounter such a scene , Want to lock a write operation on a property or method of an object , For example, in the previous example , Defined TypeScript After a variable ,name The value of a property can no longer be changed . At this time , We can add... Before the attribute name readonly Modifier syntax to mark name Is a read-only property .
interface IReadOnlyTest {
readonly name: string;
readonly age: (() => number) | undefined;
}
let ReadOnlyTest: IReadOnlyTest = {
name: 'TypeScript',
age: undefined
}
ReadOnlyTest.name = 'Change' // error TS2540: Cannot assign to 'name' because it is a read-only property.
It should be noted that , This is only read-only at the level of static type detection , In fact, it does not prevent tampering with objects . Because it is translated into JavaScript after ,readonly The modifier will be erased . therefore , Any time instead of directly modifying an object , Let's return a new object , This would be a safer practice .
Define function types
Interface types can also be used to define the types of functions ( Just defining the function type , It does not include the implementation of functions ), Specific examples are as follows .
interface ITest {
name: string;
age?:() => number
}
interface IStudy {
(language: ITest): void
}
let StudyInterface: IStudy = language => console.log(` I am learning ${
language.name}`);
StudyInterface({
name: 'TypeScript' }); // I am learning TypeScript
In the example , We define an interface type IStudy, It has an anonymous member of a function type , Function parameter type ITest, The type of return value is void, Interface types defined in this format are also called executable types , That is, a function type .
In later code , Declared a StudyInterface Variable of type , And assign it an arrow function as the value . Think about it Context type inference , To the left of the assignment operation IStudy Type can constrain the type of arrow function , So even if we don't explicitly specify function parameters language The type of ,TypeScript It can also be inferred that its type is ITest.
actually , We rarely use interface types to define the types of functions , Use more Inline type or Type the alias Use arrow function syntax to define function types , Specific examples are as follows :
type StudyInterfaceType = (language: ITest) => void
We assign an alias to the arrow function type StudyInterfaceType, It can be reused directly elsewhere StudyInterfaceType, Instead of redefining the new arrow function type definition .
Index signature
In practice , Objects are the most common types of interfaces , These objects have one thing in common , That is, all attribute names 、 The method names are determined .
actually , We often treat objects as Map Mapping uses , For example, the following code example defines the index as Arbitrary number The object of LanguageRankMap And index yes Any string The object of LanguageMap.
let LanguageRankMap = {
1: 'TypeScript',
2: 'JavaScript'
};
let LanguageMap = {
TypeScript: 2012,
JavaScript: 1995
};
This is the time , We need to use index signatures to define the object mapping structure mentioned above , And pass [ Index name : type ] The format of the constraint index .
The types of index names are divided into string and number Two kinds of , By means of LanguageRankInterface and LanguageYearInterface Two interfaces , We can describe objects whose index is any number or any string .
interface LanguageRankInterface {
[rank: number]: string;
}
interface LanguageYearInterface {
[name: string]: number;
}
let LanguageRankMap: LanguageRankInterface = {
1: 'TypeScript',
2: 'JavaScript',
'JaeWong': 3 // error TS2322: Type '{ 1: string; 2: string; JaeWong: number; }' is not assignable to type 'LanguageRankInterface'.Object literal may only specify known properties, and ''JaeWong'' does not exist in type 'LanguageRankInterface'.
}
let LanguageMap: LanguageYearInterface = {
TypeScript: 2012,
JavaScript: 1995,
2023: 2021
}
Be careful : In the example above , When numbers are used as object indexes , Its type can be compatible with numbers , It can also be compatible with strings , This is related to JavaScript Act in unison . therefore , Use 0 or ‘0’ When indexing objects , The two are equivalent .
Again , We can use readonly Annotation index signature , At this point, set the corresponding property to read-only , As shown in the following code :
interface LanguageRankInterface {
readonly [rank: number]: string;
}
interface LanguageYearInterface {
readonly [name: string]: number;
}
In the example above ,LanguageRankInterface and LanguageYearInterface Any number or string type attribute is read-only .
Be careful : Although attributes can be mixed with index signatures , But the type of the attribute must be the type of the corresponding numeric index or string index A subset of , Otherwise, an error message will appear .
Take the following example :
interface StringMap {
[prop: string]: number;
age: number;
name: string; // ts(2411) name Attribute string Type cannot be assigned to string index type number
}
interface NumberMap {
[rank: number]: string;
1: string;
0: number; // ts(2412) 0 Attribute number Type cannot be assigned to numeric index type string
}
In the example above , Because of the interface StringMap attribute name The type of string Not its corresponding string index (prop: string) type number Subset , So it will prompt an error . Empathy , Because of the interface NumberMap attribute 0 The type of number Not its corresponding numerical index (rank: number) type string Subset , So it will also prompt an error .
in addition , Because of the particularity of numeric type index mentioned above , So we can't restrict that the numeric index attribute and the string index attribute have different types , Specific examples are as follows :
interface LanguageRankInterface {
[rank: number]: string; // // ts(2413) Digital index type string Type cannot be assigned to string index type number
[prop: string]: number;
}
Here we define LanguageRankInterface Digital index of rank The type is string, And the defined string index prop The type of number Are not compatible , So there will be a prompt ts(2413) error .
Inheritance and implementation
stay TypeScript in , The interface type can Inherit and be inherited , For example, we can use the following extends Keyword implements interface inheritance .
interface ITest {
name: string;
age?:() => number;
}
interface DynamicTest extends ITest {
rank: number // Define new properties
}
interface TypeTest extends ITest {
typeChecker: string; // Define new properties
}
// Inherit multiple
interface TypeScript extends DynamicTest, TypeTest {
name: 'TypeScript';
}
In the example above , First, we defined two inheritance ITest The interface of DynamicTest and TypeTest, They will inherit ITest All attribute definitions . And then it defines that it also inherits DynamicTest and TypeTest The interface of TypeScript, It will inherit DynamicTest and TypeTest All attribute definitions , And use the same name name Attribute definitions override inherited name Attribute definitions .
Be careful : We can only override inherited properties with compatible types , As shown in the following code :
interface WrongType extends ITest {
name: number;
}
In the above code , because ITest Of name The attribute is string type ,WrongType Of name The attribute is number, The two are incompatible , So you can't inherit , It will also prompt a ts(6196) error .
We can use interface types to constrain classes , Conversely, you can use classes to implement interfaces , What is the relationship between the two ? here , We do this by using implements Keyword describes the relationship between a class and an interface .
interface ITest {
name: string;
age:() => number;
}
class TestClass implements ITest {
name: string = '';
age = () => new Date().getFullYear() - 2012
}
In the above code , class TestClass Realized ITest Interface agreement name、age Other properties and methods , If we remove name perhaps age The implementation of the , A type error will be prompted .
Type Type the alias
One function of interface types is to pull out inline types , So as to realize type reusability . Actually , We can also use the type alias to receive the extracted inline types for reuse .
here , We can do this by type Alias name = The type definition To define type aliases :
type TestType = {
name: string;
age:() => number;
}
In the above code , At first glance, it looks a bit like defining variables , It's just that here we put let 、const 、var The keyword was changed to type only .
Besides , For scenarios that cannot be overwritten by interface types , such as Combination type 、 Cross type , We can only use type aliases to receive , As shown in the following code :
interface ITest {
name: string;
age:() => number;
}
// union
type MixedType = string | number;
// cross
type IntersectionType = {
id: number; name: string; } & {
age: number; name: string; };
// Extract the interface attribute type
type AgeType = ITest['age'];
In the above code , We define a IntersectionType Type the alias , Represents the type where two anonymous interface types intersect ; At the same time, it defines a AgeType Type the alias , Represents extracted ITest age Type of attribute .
Be careful : Type the alias , As the name suggests , That is, we just give the type a new name , Instead of creating a new type .
Interface And Type The difference between
Through the above introduction , We already know that type aliases can be used to replace interface type annotations , Does this mean that the two are equivalent in the corresponding scenario ?
actually , In most cases, using interface types and type aliases is equivalent , But in some specific scenarios, there are still great differences between the two . such as , Duplicate defined interface type , Its properties will be superimposed , This feature makes it extremely convenient for us to control global variables 、 Extend the type of the third-party library , As shown in the following code :
interface ITest {
id: number;
}
interface ITest {
name: string;
}
let test: ITest = {
id: 1,
name: 'test'
}
In the above code , Two... Defined successively ITest Interface properties are superimposed together , At this point, we can assign to test A variable contains both id and name Object of property .
however , If we define type aliases repeatedly , As shown in the following code , You will be prompted with ts(2300) error :
// ts(2300) Repeated signs
type ITest {
id: number;
}
// ts(2300) Repeated signs
type ITest {
name: string;
}
let test: ITest = {
id: 1,
name: 'test'
}
边栏推荐
- P1779 Xiaohu's springboard
- Give an example to illustrate the cell and num of lstmcell in TF_ What does unit mean
- 2022 safety officer-c certificate examination practice questions simulated examination platform operation
- openGL_ 01 create window
- P5354 [Ynoi2017] 由乃的 OJ(树剖、位运算)
- (3) Data binding instructions
- P5354 [ynoi2017] yoyo OJ (tree section and bit operation)
- Format interchange among Yolo, coco and VOC datasets
- How to calculate the rarity of NFT?
- P1743 Audiophobia
猜你喜欢

Baidu AI Cloud's revenue in 2021 was RMB 15.1 billion, a year-on-year increase of 64%

openGL_03使用不同的VAO与VBO,以及不同的shader

proteus仿真Arduino
![[006] [esp32 Development Notes] steps for burning firmware using flash download tool](/img/a0/5d5e6c076d267c0ffe4c1e8f10e408.png)
[006] [esp32 Development Notes] steps for burning firmware using flash download tool
![[004] [ESP32开发笔记] 音频开发框架ADF环境搭建——基于ESP-IDF](/img/55/9eb286bc56ec991837fc014b42fc20.png)
[004] [ESP32开发笔记] 音频开发框架ADF环境搭建——基于ESP-IDF

聊聊保证线程安全的10个小技巧

Win10安装appium环境

How to calculate the rarity of NFT?
![[006] [esp32 Development notes] burn firmware steps Using Flash Download Tool](/img/a0/5d5e6c076d267c0ffe4c1e8f10e408.png)
[006] [esp32 Development notes] burn firmware steps Using Flash Download Tool

JVM面试
随机推荐
Installation and performance test of API gateway Apache APIs IX on AWS graviton3
Stepn analysis
(3)数据绑定指令
openGL_ 02 point line surface triangle
SWFUpload
API 網關 Apache APISIX 在 AWS Graviton3 上的安裝與性能測試
P5354 [ynoi2017] yoyo OJ (tree section and bit operation)
2022 safety officer-c certificate examination practice questions simulated examination platform operation
[006] [ESP32開發筆記] 使用Flash下載工具燒錄固件步驟
P5354 [Ynoi2017] 由乃的 OJ(树剖、位运算)
Golang --- comparison operation of various types of variables
渗透测试路径字典、爆破字典
Camtasia studio2022 activation code serial number
P1743 Audiophobia
API 网关 Apache APISIX 在 AWS Graviton3 上的安装与性能测试
宇邦新材深交所上市:市值47亿 第一季扣非净利降18%
Rendering pipeline ---- easy to understand and introduce to the interviewer
2022 safety officer-a certificate examination questions and online simulation examination
2022年高压电工考试模拟100题及答案
keepalived配置虚拟IP