TypeScript 学习笔记(六):索引签名类型、映射类型

news2024/11/25 10:52:35

一、索引签名类型

1. 索引类型查询操作符 keyof

keyof可以用于获取某种类型的所有键,其返回类型是联合类型。

interface Info {
  name: string;
  age: number;
}
let infoProp: keyof Info;
infoProp = "name";
infoProp = "age";
infoProp = "no"; // error 不能将类型“"no"”分配给类型“"name" | "age"”
 

通过例子可以看到,这里的keyof Info其实相当于"name" | “age”。通过和泛型结合使用,TS 就可以检查使用了动态属性名的代码:

function getValue<T, K extends keyof T>(obj: T, names: K[]): T[K][] { // 这里使用泛型,并且约束泛型变量K的类型是"keyof T",也就是类型T的所有字段名组成的联合类型
  return names.map(n => obj[n]); // 指定getValue的返回值类型为T[K][],即类型为T的值的属性值组成的数组
}
const info = {
  name: "lison",
  age: 18
};
let values: string[] = getValue(info, ["name"]);
values = getValue(info, ["age"]); // error 不能将类型“number[]”分配给类型“string[]”
 
  • 接口
interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

  • 基本数据类型
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"

class Person {
    name: string = 'lisi';
}

let sname = keyof Person
sname = 'name'

如果把sname = ‘name’改为sname = ‘yui’的话,TypeScript 编译器会提示以下错误信息:

Type '"yui"' is not assignable to type '"name"'.

报错原因:

keyof Person 获取到的类型是‘name’

来看个简单的例子:

  let a: 1;
  a = 1;
  a = 2;

上的例子,将a的类型为1,那么这个变量的值只能是1,不能为其他的,当a = 2执行的时候,就会报错
在这里插入图片描述

  • 案例
function prop(obj: object, key: string) {
  return obj[key];
}

在上面代码中,为了避免调用 prop 函数时传入错误的参数类型,我们为 obj 和 key 参数设置了类型,分别为{}string 类型。然而,针对上述的代码,TypeScript 编译器会输出以下错误信息:

在这里插入图片描述
解决方法:

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

在以上代码中,我们使用了 TypeScript 的泛型泛型约束首先定义了T类型并使用extends关键字约束该类型必须是object类型的子类型,然后使用keyof操作符获取 T 类型的所有键,其返回类型是联合类型,最后利用extends关键字约束 K 类型必须为keyof T联合类型的子类型。

type Todo = {
  id: number;
  text: string;
  done: boolean;
}

const todo: Todo = {
  id: 1,
  text: "Learn TypeScript keyof",
  done: false
}

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean
const date = prop(todo, "date"); // 会报错

变量 date 访问todo对象中不存在的属性,那么就会报错
在这里插入图片描述

a. typeof与keyof的结合

const COLORS = {
  red: 'red',
  blue: 'blue'
}

// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue',取自于k
type Colors = keyof typeof COLORS 
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok

// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error

在这里插入图片描述

b. T[K] 索引访问

  interface Eg1 {
    name: string,
    readonly age: number,
  }
  // string
  type V1 = Eg1['name']
  // string | number
  type V2 = Eg1['name' | 'age']
  // any 类型“Eg1”上不存在属性“age2222”
  type V3 = Eg1['name' | 'age2222']
  // string | number
  type V4 = Eg1[keyof Eg1]

结论:

T[keyof T]的方式,可以获取到T所有key的类型组成的联合类型;

T[keyof T]的方式,获取到的是T中的key且同时存在于K时的类型组成的联合类型;

注意:如果[]中的key有不存在T中的,则是any;因为ts也不知道该key最终是什么类型,所以是any;且也会报错。

c. & 交叉类型注意点

交叉类型取的多个类型的并集,但是如果相同key但是类型不同,则该keynever

interface Eg1 {
    name:string,
    age:number
}

interface Eg2 {
    name:string,
    age:string,
    color: string,
}

上面两个接口定义的name,age属性名是相同的,但是属性的类型并不相同:

type T = Eg1 & Eg2

T的类型为 {name: string; age: never; color: string}

注意:age因为Eg1和Eg2中的类型不一致,所以交叉后age的类型是never

2. 索引访问操作符 []

a. 操作符[]

索引访问操作符也就是[],其实和我们访问对象的某个属性值是一样的语法,但是在 TS 中它可以用来访问某个属性的类型。

b. 案例

  • 例一:
interface Info {
  name: string;
  age: number;
}
type NameType = Info["name"];
let name: NameType = 123; // error 不能将类型“123”分配给类型“string”
 
  • 例二:
function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
  return o[name]; // o[name] is of type T[K]
}

这个函数中,两个参数的类型分别为泛型 T 和 K,而函数的返回值类型为T[K],只要函数的返回值也是这种形式,即访问参数 o 的参数 name 属性,即可。

  • 例三:

与结合接口的例子:

interface Obj<T> {
  [key: number]: T;
}
const key: keyof Obj<number>; // key的类型为number

如果索引类型为 number,那么实现该接口的对象的属性名必须是 number 类型;但是如果接口的索引类型是 string 类型,那么实现该接口的对象的属性名设置为数值类型的值也是可以的,因为数值最后还是会先转换为字符串。这里一样,如果接口的索引类型设置为 string 的话,keyof Obj等同于类型number | string:

  interface Obj<T> {
    [key: string]: T;
  }
  let key: keyof Obj<number>; // key的类型为number | string
  // key = 123; // right
  key = '123'; // right
  • 例四:

也可以使用访问操作符,获取索引签名的类型:

interface Obj<T> {
  [key: string]: T;
}
const obj: Obj<number> = {
  age: 18
};
let value: Obj<number>["age"]; // value的类型是number,也就是name的属性值18的类型
  • 例五:

当tsconfig.json里strictNullChecks设为false时,通过Type[keyof Type]获取到的,是除去never & undefined & null这三个类型之后的字段值类型组成的联合类型,来看例子:

interface Type {
  a: never;
  b: never;
  c: string;
  d: number;
  e: undefined;
  f: null;
  g: object;
}
type test = Type[keyof Type];
// test的类型是string | number | object

strictNullChecks设为true时

  interface Type {
    a: never;
    b: never;
    c: string;
    d: number;
    e: undefined;
    f: null;
    g: object;
  }
  type test = Type[keyof Type];
  // test的类型是string | number | object | null | undefined 
  • 例六:
interface AnyObject {
  [key: string]: number
}
let obj: AnyObject = {
  a: 1,
  b: 2
}
// 对象obj中只能出现为number类型的值
  1. 使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。
  2. 这样,对象 obj 中就可以出现任意多个属性(比如,a、b等)。
  3. key 只是一个占位符,可以换成任意合法的变量名称。
  4. 隐藏的前置知识:JS 中对象({})的键是 string 类型的。
  • 例七:

数组对应的泛型接口:

interface MyArray<T> {
  [n: number]: T   // 数组中每一项都是T类型(T是该数组的类型变量)
}

let arr: MyArray<number> = [1, 3, 5]   
// 数组中每一项都是number类型,换句话说,这是一个number[]

  1. MyArray 接口模拟原生的数组接口,并使用 [n: number]来作为索引签名类型。
  2. 该索引签名类型表示:只要是 number 类型的键(索引)都可以出现在数组中,或者说数组中可以有任意多个元素。
  3. 同时也符合数组索引是 number 类型这一前提。

二、映射类型

1. 概述

根据的类型创建出的类型, 我们称之为映射类型

2. 基础案例

我们可以使用这个接口实现一个有且仅有一个 age 属性的对象,但如果我们想再创建一个只读版本的同款对象,那我们可能需要再重新定义一个接口,然后让 age 属性 readonly。如果接口就这么简单,你确实可以这么做,但是如果属性多了,而且这个结构以后会变,那就比较麻烦了。这种情况我们可以使用映射类型,下面来看例子:

interface Info {
  age: number;
}
type ReadonlyType<T> = { readonly [P in keyof T]: T[P] }; // 这里定义了一个ReadonlyType<T>映射类型
type ReadonlyInfo = ReadonlyType<Info>;
let info: ReadonlyInfo = {
  age: 18
};
info.age = 28; // error Cannot assign to 'age' because it is a constant or a read-only property
 

在这里插入图片描述
这个例子展示了如何通过一个普通的接口创建一个每个属性都只读的接口,这个过程有点像定义了一个函数,这个函数会遍历传入对象的每个属性并做处理。同理你也可以创建一个每个属性都是可选属性的接口:

  interface Info {
    age: number;
  }
  type ReadonlyType<T> = { readonly [P in keyof T]?: T[P] };
  type ReadonlyInfo = ReadonlyType<Info>;
  let info: ReadonlyInfo = {age: 18};
  console.log(info)

注意 :
这里用到了一个新的操作符 in,TS 内部使用了 for … in,定义映射类型,这里涉及到三个部分:

  • 类型变量,也就是上例中的 P,它就像 for…in 循环中定义的变量,用来在每次遍历中绑定当前遍历到的属性名;
  • 属性名联合,也就是上例中keyof T,它返回对象 T 的属性名联合;
  • 属性的结果类型,也就是 T[P]。

实际上,TS 内置了这两种映射类型,泛型工具类型(比如,Partial< Type>、Record<Keys,Type>)

type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Record<K extends keyof any, T> = { [P in K]: T };
 

Pick 例子:

interface Info {
  name: string;
  age: number;
  address: string;
}
const info: Info = {
  name: "lison",
  age: 18,
  address: "beijing"
};
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { // 这里我们定义一个pick函数,用来返回一个对象中指定字段的值组成的对象
  let res = {} as Pick<T, K>;
  keys.forEach(key => {
    res[key] = obj[key];
  });
  return res;
}
const nameAndAddress = pick(info, ["name", "address"]); // { name: 'lison', address: 'beijing' }
 

Record例子:
它适用于将一个对象中的每一个属性转换为其他值的场景。

function mapObject<K extends string | number, T, U>(
  obj: Record<K, T>,
  f: (x: T) => U
): Record<K, U> {
  let res = {} as Record<K, U>;
  for (const key in obj) {
    res[key] = f(obj[key]);
  }
  return res;
}
 
const names = { 0: "hello", 1: "world", 2: "bye" };
const lengths = mapObject(names, s => s.length); // { 0: 5, 1: 5, 2: 3 }
 

我们输入的对象属性值为字符串类型,输出的对象属性值为数值类型。

keyof 和映射类型支持用 number 和 symbol 命名的属性的例子:

const stringIndex = "a";
const numberIndex = 1;
const symbolIndex = Symbol();
type Obj = {
  [stringIndex]: string;
  [numberIndex]: number;
  [symbolIndex]: symbol;
};
type keys = keyof Obj;
let key: keys = 2; // error
let key: keys = 1; // right
let key: keys = "b"; // error
let key: keys = "a"; // right
let key: keys = Symbol(); // error
let key: keys = symbolIndex; // right
const stringIndex = "a";
const numberIndex = 1;
const symbolIndex = Symbol();
type Obj = {
  [stringIndex]: string;
  [numberIndex]: number;
  [symbolIndex]: symbol;
};
type ReadonlyType<T> = { readonly [P in keyof T]?: T[P] };
let obj: ReadonlyType<Obj> = {
  a: "aa",
  1: 11,
  [symbolIndex]: Symbol()
};
obj.a = "bb"; // error Cannot assign to 'a' because it is a read-only property
obj[1] = 22; // error Cannot assign to '1' because it is a read-only property
obj[symbolIndex] = Symbol(); // error Cannot assign to '[symbolIndex]' because it is a read-only property
 

元组和数组上的映射类型:

在元组和数组上的映射类型会生成新的元组和数组,并不会创建一个新的类型,这个类型上会具有 push、pop 等数组方法和数组属性。来看例子:

type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> };
type Tuple = [number, string, boolean];
type promiseTuple = MapToPromise<Tuple>;
let tuple: promiseTuple = [
  new Promise((resolve, reject) => resolve(1)),
  new Promise((resolve, reject) => resolve("a")),
  new Promise((resolve, reject) => resolve(false))
];

这个例子中定义了一个MapToPromise映射类型。它返回一个将传入的类型的所有字段的值转为Promise,且Promise的resolve回调函数的参数类型为这个字段类型。我们定义了一个元组Tuple,元素类型分别为number、string和boolean,使用MapToPromise映射类型将这个元组类型传入,并且返回一个promiseTuple类型。当我们指定变量tuple的类型为promiseTuple后,它的三个元素类型都是一个Promise,且resolve的参数类型依次为number、string和boolean。

2.1 同态

两个相同类型的代数结构之间的结构保持映射。

这四个内置映射类型中,Readonly、Partial 和 Pick 是同态的,而 Record 不是,因为 Record 映射出的对象属性值是新的,和输入的值的属性值不同。

3. 由映射类型进行推断

还原映射之前的类型, 这种操作我们称之为 拆包

type Proxy<T> = { // 这里定义一个映射类型,他将一个属性拆分成get/set方法
  get(): T;
  set(value: T): void;
};
type Proxify<T> = { [P in keyof T]: Proxy<T[P]> }; // 这里再定义一个映射类型,将一个对象的所有属性值类型都变为Proxy<T>处理之后的类型
function proxify<T>(obj: T): Proxify<T> { // 这里定义一个proxify函数,用来将对象中所有属性的属性值改为一个包含get和set方法的对象
  let result = {} as Proxify<T>;
  for (const key in obj) {
    result[key] = {
      get: () => obj[key],
      set: value => (obj[key] = value)
    };
  }
  return result;
}
let props = {
  name: "lison",
  age: 18
};
let proxyProps = proxify(props);
console.log(proxyProps.name.get()); // "lison"
proxyProps.name.set("li");
 

我们来看下这个例子,这个例子我们定义了一个函数,这个函数可以把传入的对象的每个属性的值替换为一个包含 get 和 set 两个方法的对象。最后我们获取某个值的时候,比如 name,就使用 proxyProps.name.get()方法获取它的值,使用 proxyProps.name.set()方法修改 name 的值。

接下来我们来看如何进行拆包:

function unproxify<T>(t: Proxify<T>): T { // 这里我们定义一个拆包函数,其实就是利用每个属性的get方法获取到当前属性值,然后将原本是包含get和set方法的对象改为这个属性值
  let result = {} as T;
  for (const k in t) {
    result[k] = t[k].get(); // 这里通过调用属性值这个对象的get方法获取到属性值,然后赋给这个属性,替换掉这个对象
  }
  return result;
}
let originalProps = unproxify(proxyProps);
 

4. 增加或移除特定修饰符

映射类型增加了增加或移除特定修饰符的能力,使用+和-符号作为前缀来指定增加还是删除修饰符。首先来看我们如何通过映射类型为一个接口的每个属性增加修饰符,我们这里使用+前缀:

interface MyInterface {
    name: string;
    age: number;
}

type MyType<T> = {
    +readonly [P in keyof T]: T[P];
}

type test = MyType<MyInterface>;

type UnMyType<T> = {
    -readonly [P in keyof T]: T[P];
}

type test2 = UnMyType<test>;

映射类型增加了增加或移除特定修饰符的能力,使用+和-符号作为前缀来指定增加还是删除修饰符。

首先来看我们如何通过映射类型为一个接口的每个属性增加修饰符,我们这里使用+前缀:

interface Info {
  name: string;
  age: number;
}
type ReadonlyInfo<T> = { +readonly [P in keyof T]+?: T[P] };
let info: ReadonlyInfo<Info> = {
  name: "lison"
};
info.name = ""; // error

在这里插入图片描述

ReadonlyInfo创建的接口类型,属性是可选的,所以我们在定义 info 的时候没有写 age 属性也没问题,同时每个属性是只读的,所以我们修改 name 的值的时候报错。我们通过+前缀增加了 readonly 和?修饰符。当然,增加的时候,这个+前缀可以省略,也就是说,上面的写法和type ReadonlyInfo = { readonly [P in keyof T]?: T[P] }是一样的。我们再来看下怎么删除修饰符:

interface Info {
  name: string;
  age: number;
}
type RemoveModifier<T> = { -readonly [P in keyof T]-?: T[P] };
type InfoType = RemoveModifier<Readonly<Partial<Info>>>;
let info1: InfoType = {
  // error missing "age"
  name: "lison"
};
let info2: InfoType = {
  name: "lison",
  age: 18
};
info2.name = ""; // right, can edit

这个例子我们定义了去掉修饰符的映射类型 RemoveModifier,Readonly<Partial>则是返回一个既属性可选又只读的接口类型,所以 InfoType 类型则表示属性必含而且非只读。

TS 内置了一个映射类型Required,使用它可以去掉 T 所有属性的?修饰符。

5. 重映射

重映射就是在索引后加一个 as 语句,表明索引转换成什么,它可以用来对索引类型做过滤和转换。

返回 never 代表过滤掉,否则保留。

 type person ={
  name:'lisi',
  age:20,
  gender:true,
 }
// 比如过滤出类型为 string 的索引:
 type FilterString<T> = {
  [Key in keyof T as T[Key] extends string ? Key: never]: T[Key];
}
type res =FilterString<person>

在这里插入图片描述
还可以对索引做转换,比如修改索引名,加上 get:

 type person ={
  name:'lisi',
  age:20,
  gender:true,
 }

type Getters<T extends Record<any, any>> = {
  [Key in keyof T as `get${Capitalize<Key & string>}`]: T[Key];
}
type res =Getters<person>

在这里插入图片描述
T extends xxx 是给类型参数的约束,表示只能传入这种类型。这里的 Record 类型是生成索引类型的,我们上面介绍过,所以 T extends Record<any, any> 就是约束了这里只能传入索引类型。

as 后面是把索引转换成什么,我们是在原来的基础上做了修改,加上了 get,并且后面内容首字母大写,这个 Capitalize 也是 TS 内置的类型。

这两个例子分别说明了 重映射 as 可以用来做索引类型的过滤和转换,可以对索引类型做更灵活的编程。

实现 key 和 value 的互换:

type person ={
  name:'lisi',
  age:20,
  gender:true,
 }

 type Flip<T extends Record<any, any>> = {
  [Key in keyof T as `${T[Key]}`]: Key
}
type res =Flip<person>

在这里插入图片描述
支持重映射之后,映射类型可以对索引类型做更多的修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/765917.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识普及:Boc-Hynic,133081-25-1,6-叔丁氧羰肼基-3-吡啶甲酸,

&#xff08;文章资料汇总来源于&#xff1a;陕西新研博美生物科技有限公司小编MISSwu&#xff09;​ Boc-Hynic&#xff0c;6-[2-(tert-Butoxycarbonyl)hydrazinyl]nicotinic acid&#xff0c;6-[2-(叔丁氧羰基)肼基]烟酸&#xff0c;6-叔丁氧羰肼基-3-吡啶甲酸 ------------…

追梦之旅【数据结构篇】——C语言手撕八大经典排序

追梦之旅【数据结构篇】——C语言手撕八大经典排序&#x1f60e; 前言&#x1f64c;排序的认识排序的稳定性&#xff1a;排序的时间复杂度和空间复杂度以及如何选择适合的排序&#xff1a; 优化版选择排序冒泡排序普通版冒泡排序升级版冒泡排序 直接插入排序希尔排序堆排序快速…

ChatGPT变现五个思路

一、前言 ChatGPT是一款AI聊天机器人&#xff0c;发布于2022年11月。凭借着在广泛的知识领域为消费者问题做出清晰、详尽解答的出色能力&#xff0c;其一经推出就引发全球轰动&#xff0c;自然也得到零售行业的高度关注。例如&#xff0c;消费者只要询问ChatGPT如何布置一个梦…

将Spring Boot项目打包部署到阿里云linux服务器

首先 你要保证自己的服务器上有java环境 如果没有可以参考我的文章 linux服务器中安装java JDK1.8版本 然后 我们打开我们的Spring Boot项目 双击 package 生命周期进行打包 打包完成之后 我们找到 target 下面会有一个jar包 然后 我们右键它 如下图操作 系统就会帮你打开它所…

javascript 导出表格的excel

一个php网站的表格,需要增加导出excel的功能, 因对web开发不甚了解,开始想着用php导出, 搜索一番发现比较复杂,而且我的表格里已经有数据了, 如果导出又要去库中获取一次,不是负担加倍, 可否把现有表格数据,直接导出来? 答案是肯定的,用js在前端导出 开源js组件…

三个属性让你学会书写横向滑动窗口!内附代码和详解!

先说结论&#xff1a; 父组件添加&#xff1a; display: flex; overflow-x: auto; 子组件添加&#xff1a; flex-shrink: 0; 下面进行详细讲述。 在书写滑动页面之前&#xff0c;最好了解一下flex布局的基本原理和常用属性&#xff0c;以下链接介绍较详细&#xff0c;图文并…

ROS1 ROS2学习

ROS1 ROS2学习 安装 ROSROS1ROS2 命令行界面ROS2 功能包相关指令ROS 命令行工具ROS1 CLI工具ROS2 CLI工具 ROS 通信核心概念节点 - Node节点相关的CLI 话题 - Topic编写发布者程序流程&#xff1a;编写订阅者程序流程&#xff1a;话题相关的CLI 服务 - Service编写客户端程序流…

CVE-2013-4547

CVE-2013-4547 一、环境搭建二、漏洞原理三、漏洞复现 一、环境搭建 如下介绍kali搭建的教程 cd ~/vulhub/nginx/CVE-2013-4547 // 进入指定环境 docker-compose build // 进行环境编译 docker-compose up -d // 启动环境docker-compose ps使用这条命令查看当前正在运…

v-for复习

 在真实开发中&#xff0c;我们往往会从服务器拿到一组数据&#xff0c;并且需要对其进行渲染。  这个时候我们可以使用 v-for 来完成&#xff1b;  v-for 类似于 JavaScript 的 for 循环&#xff0c;可以用于遍历一组数据&#xff1b; 1.v-for 基本使用  v-for 的基本…

水雨情自动监测系统-水雨情监测设备

近年来&#xff0c;暴雨及极端天气越来越频发&#xff0c;造成了洪涝与积水灾害给各地排水相关部门带来了巨大的压力&#xff0c;也给公众的生命财产损失带来了巨大的风险。为降低洪涝造成的损失&#xff0c;我们不仅要加强排水基础建设&#xff0c;还要提升实时监测手段&#…

一、音频基础-音频分析的重要工具(语谱图)

文章目录 1. 傅里叶转换2. 语谱图3. 应用1. 傅里叶转换 通过前面的描述可以知道,声音的本质就是各种声波,那么任意某一个时刻,都不可能是只有一个频率的波,而且声波也不可能是我们理解的标准的正弦波: 而一般我们对声音进行处理时,需要分析出频率当中的有哪些频率,然…

EasyPlayer流媒体视频播放器宽屏模式的开发与实现

EasyPlayer流媒体视频播放器可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持RTSP、RTMP、HLS、FLV、WebRTC等格式的视频流播放&#xff0c;并且已实现网页端实时录像、在iOS上实现低延时直播等功能。 EasyPlayer.js播放器目前可支持全屏播放&…

mybatis-plus Integer类型null值无法修改的问题

我们来看一条数据&#xff0c; 我现在要更新这个数据&#xff0c;除了id&#xff0c;全部设为null. 我们来看下数据库&#xff1a; 这个age很明显没有设置成null. 这是因为这个age是Integer类型的&#xff0c;而且我们使用的是mybatis的原生方法&#xff0c; 解决方案&#xf…

抖音seo源码/抖音seo优化矩阵系统代开发源代码搭建

抖音seo源码/抖音seo源码代开发/抖音seo技术搭建系统应用代码编程&#xff1a; 抖音seo源码&#xff0c;抖音seo矩阵系统底层框架上支持了从ai视频混剪&#xff0c;视频批量原创产出&#xff0c;云端直播数字人视频制作&#xff0c;账号矩阵&#xff0c;视频一键分发&#xff…

【学会动态规划】不同路径 II(6)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

v-model绑定的数据与接收到的数据类型不一致引发的bug

在使用v-model的过程中 当页面渲染需要的数据类型与data中定义的数据类型不一致时&#xff0c;页面是不会进行响应式对应渲染的、 如:1:绑定的是string的时候&#xff0c;在定义时是number类型 也会导致页面不更新 2:列表下拉框的选中的数据若定义的是number的话&#xff0c;传…

静电接地桩的使用和维护

静电接地桩&#xff0c;也称为静电防护接地桩或静电消散接地桩&#xff0c;是一种用于防止静电积聚和降低电荷积聚的设备。它主要通过将静电荷导引到地下&#xff0c;实现静电的释放和中和。 静电接地桩通常由导电材料制成&#xff0c;如铜、铝等金属材料。它们通常以垂直方式…

目标追踪的方向分析

方向分析 目标运动方向分析的一种最常用方法是光流法&#xff0c;光流法通过相邻两帧图像中光流近似目标的运动。光流法比较适于估计较短时间内的目标运动趋势&#xff08;如相邻几帧&#xff09;&#xff0c;且光流法对图像噪声非常敏感&#xff0c;如下图&#xff0c;为…

矩阵AB和BA的特征值相同

手写的&#xff0c;如下图&#xff1a; 即可证明&#xff0c;矩阵AB的特征值和BA的特征值相同。 关于矩阵转置和逆矩阵混合运算&#xff0c;有如下规律&#xff1a;

Ubuntu关闭自动休眠

一、查看当前休眠模式 使用systemctl status sleep.target 命令查看当前休眠模式&#xff0c;结果如下图&#xff0c;sleep状态为enabled&#xff0c;表示自动休眠模式开启。 二、关闭自动休眠模式 使用sudo systemctl mask sleep.target suspend.target 关闭休眠模式 三…