1. 报错信息展示
给el-inut绑定model值时
报错:不能将类型“{ readonly [x: number]: string; toString: () => string; charAt: (pos: number) => string; charCodeAt: (index: number) => number; concat: (...strings: string[]) => string; indexOf: (searchString: string, position?: number | undefined) => number; ... 42 more ...; [Symbol.iterator]: () => IterableIterator<...>; } | unde...”分配给类型“EpPropMergeType<(new (...args: any[]) => string | number) | (() => string | number | null | undefined) | ((new (...args: any[]) => string | number) | (() => string | number | null | undefined))[], unknown, unknown>”。
input.vue.d.ts(211, 14): 所需类型来自属性 "modelValue",在此处的 "Partial<{ readonly disabled: boolean; readonly id: string; readonly type: string; readonly modelValue: EpPropMergeType<(new (...args: any[]) => string | number) | (() => string | number | null | undefined) | ((new (...args: any[]) => string | number) | (() => string | ... 2 more ... | undefined))[], unknown, unknown..." 类型上声明该属性
2. 报错可能性分析
好久前学的ts 没使用之后忘差不多了,js的类型和原型也没之前那么重视; 刚开始在想 怎么这么长一大段,于是猜测报错的可能性是什么:
- 接口的使用没掌握好。
- 类型断言也没搞懂。
- element-plus版本。
- 定义类型不对。
接口的使用
定义一个接口通过关键词interface实现,此时可以对接口的属性进行类型设置。
1. 接口的扩展
用于将一个接口的属性和方法继承到另一个接口中 。当一个接口中需要用到另一个属性及其方法时,可通过extends扩展第一个接口
// 定义第一个接口
interface InterfaceA {
id: number;
name: string;
}
// 定义第二个接口,扩展第一个接口
interface InterfaceB extends InterfaceA {
description: string;
isActive: boolean;
}
// 使用示例
const example: InterfaceB = {
id: 1,
name: 'Example',
description: 'This is an example',
isActive: true,
};
2. 接口的合并
用于将多个接口合并成一个接口
// 定义第一个接口
interface InterfaceA {
id: number;
name: string;
}
// 定义第二个接口
interface InterfaceB {
description: string;
isActive: boolean;
}
// 合并接口
interface CombinedInterface extends InterfaceA, InterfaceB {}
// 使用示例
const example: CombinedInterface = {
id: 1,
name: 'Example',
description: 'This is an example',
isActive: true,
};
3. 类型别名
灵活地组合接口类型
// 定义第一个接口
interface InterfaceA {
id: number;
name: string;
}
// 定义第二个接口
interface InterfaceB {
description: string;
isActive: boolean;
}
// 组合接口类型
type CombinedType = InterfaceA & InterfaceB;
// 使用示例
const example: CombinedType = {
id: 1,
name: 'Example',
description: 'This is an example',
isActive: true,
};
4. 读写属性
默认属性是读写的,用 readonly
来定义只读属性
// 只读接口
interface ReadOnlyInterface {
readonly id: number;
readonly name: string;
}
// 可读写接口
interface ReadWriteInterface extends ReadOnlyInterface {
description: string;
isActive: boolean;
}
// 使用示例
const example: ReadWriteInterface = {
id: 1,
name: 'Example',
description: 'This is an example',
isActive: true,
};
// 只读属性不能被修改
example.id = 2; // 错误:id 是只读属性
5. 嵌套对象类型
使得属性的值符合指定接口的结构 提高代码复用性和类型安全性,同时也支持灵活的读写权限管理
// 可读写属性
interface InterfaceA {
id: number;
name: string;
}
// 定义第二个接口,info 属性是 InterfaceA 类型,但标记为只读
interface InterfaceB {
readonly info: InterfaceA; // info 属性是只读的
description: string;
}
const example: InterfaceB = {
info: {
id: 1,
name: 'John Doe'
},
description: 'A description'
};
// 只能读取 info,不能修改
example.info.name = 'Jane Doe'; // 错误:info 是只读的
类型断言
TypeScript 提供了两种类型断言的语法:
- 尖括号语法(旧语法):
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
as
语法(推荐语法):
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
建议使用 as
语法,因为它与 JSX 语法更兼容,并且更具可读性。
示例
1. 基本类型断言
当你有一个 any
类型的变量,且你知道它实际是一个 string
类型:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length; // 使用 'as' 语法
// 或
let strLength2: number = (<string>someValue).length; // 使用尖括号语法
2. 断言为对象类型
当你知道某个对象符合特定接口或类型,但 TypeScript 编译器无法推断:
interface Person {
name: string;
age: number;
}
let obj: any = { name: "John", age: 30 };
// 断言 obj 为 Person 类型
let person = obj as Person;
console.log(person.name); // John
console.log(person.age); // 30
3. 处理 DOM 元素
在处理 DOM 元素时,TypeScript 可能不知道具体的元素类型。你可以使用类型断言来指定:
let inputElement = document.querySelector('input') as HTMLInputElement;
inputElement.value = "New Value";
4. 函数类型断言
当你需要断言某个函数的返回类型:
function getValue(): any {
return "some value";
}
let value = getValue() as string; // 断言返回值为 string 类型
console.log(value.length);
注意事项
- 类型断言并不会进行类型检查:它仅仅是告诉 TypeScript 编译器“相信我,我知道这个值是什么类型”。实际运行时如果类型不匹配,可能会导致错误。
- 使用类型断言时要小心:确保你对断言的类型是正确的,否则可能会引发运行时错误。
- 避免过度使用:过度使用类型断言可能会隐藏真实的类型错误,导致难以维护的代码。尽量依赖于 TypeScript 的类型推断和类型检查功能,只有在确实需要时使用类型断言。
element-plus版本
检查了一下也是适配的
"element-plus": "^2.8.3",
"vue": "^3.4.37"
定义类型不对
export interface wmFont {
color?: String // 水印文字颜色
fontSize?: String // 水印文字大小
fontStyle?: 'none' | 'normal' | 'italic' | 'oblique' // 水印文字样式
fontWeight?: 'normal' | 'light' | 'weight' | number // 水印文字粗细
fontFamily?: String // 水印文字字体
}
可能大家一眼就看出来了问题所在。但是我当时眼瞎是真找不着而且也没往这方面想。
前前后后查看了接口定义,接口嵌套,已经定义的变量类型均无果。
此时看到了报错提示:
[x: number]: string;[1]
toString: () => string;
charAt: (pos: number) => string;
这些不都是String对象上的方法吗 这就不得不提string和String的区别了
解决问题关键
string和String的区别
简单来说:string表示原生类型,而String表示对象。
string
(基本类型)
string
是 TypeScript 中的原始类型(primitive type),它表示的是 JavaScript 中的基本数据类型——字符串。- 当你在 TypeScript 中使用
string
时,它表示的是一个基本的、不可变的字符串值。
示例:
let basicString: string = "Hello, world!";
在这个例子中,basicString
是一个简单的字符串。
String
(对象类型)
String是不可变对象,这意味着对String对象的任何操作,如修改、连接、替换等,实际上都是通过创建新的String对象来实现的,而不是直接修改原始对象。这种设计使得String对象具有线程安全性和内存安全性,因为多个线程可以同时访问和操作同一个不可变的String对象,而不会出现数据不一致的问题。
这也是报错的根本原因所在。
String
是 JavaScript 中的内置对象类型(object type),它是string
类型的封装对象。当你使用new String()
时,它会创建一个字符串对象,而不是一个基本的字符串值。String
通过构造函数new String()
创建,属于引用类型,而不是基本类型。
示例:
let objectString: String = new String("Hello, world!");
在这个例子中,objectString
是一个字符串对象,而不是基本的字符串值。
区别总结:
string
是原始类型,表示基本的字符串值,没有任何的方法,性能更好,通常应该优先使用。String
是对象类型,表示包装后的字符串对象,除非有特殊需求(如需要使用某些对象方法),否则不建议使用。
使用建议:
一般情况下,应该使用 string
而不是 String
,因为 string
是更轻量、更高效的基本类型,而 String
是一个对象,会增加不必要的性能开销。
举例:
let basicString: string = "hello";
let objectString: String = new String("hello");
console.log(typeof basicString); // "string"
console.log(typeof objectString); // "object"
如上所示,basicString
是 string
类型,而 objectString
是 object
类型。
3. 解决措施
将接口的类型定义进行修改。将String改为string
export interface wmFont {
color?: string // 水印文字颜色
fontSize?: string // 水印文字大小
fontStyle?: 'none' | 'normal' | 'italic' | 'oblique' // 水印文字样式
fontWeight?: 'normal' | 'light' | 'weight' | number // 水印文字粗细
fontFamily?: string // 水印文字字体
}
4. 另外
[1] interface StringArray
是一个 TypeScript 的接口定义,它表示一个数组,数组中的每个元素都是字符串类型。
interface StringArray {
[index: number]: string;
}
含义
interface StringArray
: 定义了一个名为StringArray
的接口。[index: number]: string;
: 这是一个索引签名。它表示用任意数字类型的索引(即index
),可以访问到一个string
类型的值。
用法
const myArray: StringArray = ["苹果", "香蕉", "樱桃"];
console.log(myArray[0]); // 输出: 苹果
console.log(myArray[1]); // 输出: 香蕉
StringArray
接口定义了一个用数字作为索引,字符串作为值的数组结构。实际上,它和 TypeScript 内置的 string[]
类型是相同的