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的全部屬性

如此,可以避免編譯不知道泛型的具體類型,而出現運行時不具備需要的屬性而出錯。通過繼承實現泛型類型的屬性約束。

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