TypeScript泛型高级使用

在代码开发中,使用泛型可以提高代码复用率和精简代码量。
TypeScript基础类型的泛型使用这里不做说明,网上很多资料。以下遇到的问题,文档中有说明,但没有明确的例子,开发中工具报的错误,不是很理解,记录错误解决如下。

在使用TypeScript中使用泛型,遇到如下问题:

interface TestData {
	name: string;
	num: number;
}

interface test<T> {
	getData(): T;
}

abstract AbstractTest<T> implements test<T> {
	data: T
	constructor(data: T) {
		this.data = data
	}
	abstract getData(): T;
}

class ATest<TestData> extends AbstractTest<T> {
	constructor() {
		let data: TestData = {
			name: "bin",
			num: 0
		}
		super(data)
	}
	getData(): T {
		return this.data;
	}
}

编译以上代码会出现如下问题:

不能将类型“{ name: string; num: number; }”分配给类型“TestData ”。
‘{ name: string; num: number; }’ is assignable to the constraint of type ‘TestData’, but ‘TestData’ could be instantiated with a different subtype of constraint ‘{}’.ts(2322)

上面的字面意思是:

{ name: string; num: number; }可以分配给受类型TestData约束类型,但是TestData也可以被约束的另一个子类型“{}”实例化。

从测试角度分析,T可以被多个类型初始化,不是唯一的TestData,因此不具备唯一性和准确性。

参照官方文档,实例如下:

function loggingIdentity<T>(arg: T): T {
  console.log(arg.length); // Error: T doesn't have .length
  return arg;
}
若传入的T类型没有length,会在运行时出错。若要避免该问题,T类型继承一个一定有length的类型
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

loggingIdentity(3); // Error, number doesn't have a .length property
loggingIdentity({ length: 10, value: 3 });

按照官方文档理解,若要使泛型具备确定的属性,泛型需要继承指定的类型。以上demo改为一下:

interface TestData {
	name: string;
	num: number;
}

interface test<T> {
	getData(): T;
}

abstract AbstractTest<T> implements test<T> {
	data: T
	constructor(data: T) {
		this.data = data
	}
	abstract getData(): T;
}

class ATest<T extends TestData> extends AbstractTest<TestData> {
	constructor() {
		let data: TestData = {
			name: "bin",
			num: 0
		}
		super(data)
	}
	getData(): T {
		return this.data;
	}
}

let test = ATest<TestData>()
test.getData()

如此就不会出错了。
如果data赋值的对象多一个属性,也不会出错,比如

let data: = {
	name: "bin",
	num: 0,
	test: 0
}

从测试情况看,为了避免类型错误,赋值给泛型对象的变量的属性必须要包含确定类型的全部属性,通过类型继承即可实现该需求,比如:

interface A {
	a: string;
	b: number;
}
<T extends A>
T就包含A的全部属性

如此,可以避免编译不知道泛型的具体类型,而出现运行时不具备需要的属性而出错。通过继承实现泛型类型的属性约束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章