what is the difference between a function with generic type parameter and non-generic function in typescript?

huangapple go评论70阅读模式
英文:

what is the difference between a function with generic type parameter and non-generic function in typescript?

问题

I recently came across a scenario where I thought I might need to use generics and looked into the typescript documentation on generics to understand it correctly. but I am still not clear about the different use cases for using generics over non generics.

以下是要翻译的内容:

interface Type1 {
  name: string,
  age: 23
}

const function1 = (each: Type1) => {
   // some logic...
   return 'something';
}

interface Type2 {
  name: string,
  age: 23
}

const function2 = <Type2>(each: Type2) => {
   // some logic...
   return 'something';
}

I read the documentation for generics multiple times and tried to find the difference in code playground

update 1

const genericTest = <T>(arg: T): T => { return arg; };
const genericTest1 = <T extends Test1>(arg: T): T => { 
console.log(arg.name); return arg; }; // this will work
const genericTest2 = <T>(arg: T): T => { console.log(arg.name); return arg; }; // error: Property 'name' does not exist on type 'T'

interface Test1 {
    name: string,
    age: number
}
const result1 = genericTest<Test1>({ name: 'vishnu', age: 29 });
console.log('result1::> ', result1);

what are some of the use-cases of unrestricted generics (without using extends)??
if I am using unrestricted generics, how can i use the arg inside the function?

Update 2

I found a implementation of a custom array.find method and converted it into generics function. I think I have a pretty good idea of what generics are used for.

loosely speaking, generics is about the ability to handle multiple types safely rather then the type itself

interface Array<T> {
customFind(o: CB<T>): T | undefined;
}
interface CB<T> {
    (each: T, index?: number, array?: Array<T>): boolean;
}
Array.prototype.customFind = function<T>(callback: CB<T>) {
  for (let i = 0; i < this.length; i++) if(callback(this[i], i, this)) return this[i]
}
const array: number[] = [1,2,3,4,5,6,7]
const result = array.customFind((each)=>{
    return each===1
})
console.log(result);
英文:

I recently came across a scenario where I thought I might need to use generics and looked into the typescript documentation on generics to understand it correctly. but I am still not clear about the different use cases for using generics over non generics.

what is the difference between the following snippets?

interface Type1 {
  name: string,
  age: 23
}

const function1 = (each: Type1) => {
   // some logic...
   return 'something';
}

and

interface Type2 {
  name: string,
  age: 23
}

const function2 = <Type2>(each: Type2) => {
   // some logic...
   return 'something';
}

I read the documentation for generics multiple times and tried to find the difference in code playground

update 1

const genericTest = <T>(arg: T): T => { return arg; };
const genericTest1 = <T extends Test1>(arg: T): T => { 
console.log(arg.name); return arg; }; // this will work
const genericTest2 = <T>(arg: T): T => { console.log(arg.name); return arg; }; // error: Property 'name' does not exist on type 'T'

interface Test1 {
    name: string,
    age: number
}
const result1 = genericTest<Test1>({ name: 'vishnu', age: 29 });
console.log('result1::> ', result1);

what are some of the use-cases of unrestricted generics (without using extends)??
if I am using unrestricted generics, how can i use the arg inside the function?

Update 2

I found a implementation of a custom array.find method and converted it into generics function. I think I have a pretty good idea of what generics are used for.

loosely speaking, generics is about the ability to handle multiple types safely rather then the type itself

interface Array<T> {
customFind(o: CB<T>): T | undefined;
}
interface CB<T> {
    (each: T, index?: number, array?: Array<T>): boolean;
}
Array.prototype.customFind = function<T>(callback: CB<T>) {
  for (let i = 0; i < this.length; i++) if(callback(this[i], i, this)) return this[i]
}
const array: number[] = [1,2,3,4,5,6,7]
const result = array.customFind((each)=>{
    return each===1
})
console.log(result);

答案1

得分: 1

A generic type parameter in a function is useful when you need to reference or reuse a type in some way. In your examples you do not do this.

But let's say you have a function that logs a value and then returns it.

Without generic the best you could is this:

const logAndReturn = (arg: unknown): unknown => {
  console.log(arg)
  return arg
}

const userA: User = { name: 'Foo', age: 30 }
const userB: User = logAndReturn(userA) // error: cannot assign unknown to user

Now with generics:

const logAndReturn = <T>(arg: T): T =&gt; {
  console.log(arg)
  return arg
}

const userA: User = { name: 'Foo', age: 30 }
const userB: User = logAndReturn(userA) // fine

Now if I pass in a User I get a User back, but the function itself doesn't care what I pass in. So this function needs to be generic because the return type depends on the type of arg.


Another example, say you want a random element from an array.

function getRandomElement<T>(arr: T[]): T {
  const index = Math.floor(Math.random() * arr.length)
  return arr[index]
}

const users: User[] = [...]
const randomUser = getRandomElement(users) // returns object of type: User

Here you declare a generic T to be the type for members of the array. Accept an argument that is an array of T, and then return T.

Again, you need a generic here because the return type depends on a type that will only be known when the function is called with a specific type elsewhere in your code.


> if I am using unrestricted generics, how can i use the arg inside the function?

Your function typically won't care what the type is, but something downstream will. For example, in my getRandomElement function above it doesn't matter what the elements in the array actually are to this function, but that just sets the type that will be returned.

If you want to access specific fields of those values, then that is when you need to constrain it.

function filterCool<T extends { isCool: boolean }>(arr: T[]): T {
  return arr.filter(item => item.isCool)
}

const users = [{ name: "Abc", isCool: true }, { name: "Def", isCool: false }]
const coolUsers = filterCool(users) // { name: string, isCool: boolean }[]

Here we use T extends { isCool: boolean } because the function requires isCool to work, but we want to allow other fields as well.


> loosely speaking, generics is about the ability to handle multiple types safely rather then the type itself

A better way to say it is that generics allow you to accept a type that may differ throughout your program, and it allows you to reference whatever that type is elsewhere.

When one type in a function depends on another type in that function, that usually means you want a generic function.

英文:

A generic type parameter in a function is useful when you need to reference or reuse a type in some way. In your examples you do not do this.

But let's say you have a function that logs a value and then returns it.

Without generic the best you could is this:

const logAndReturn = (arg: unknown): unknown =&gt; {
  console.log(arg)
  return arg
}

const userA: User = { name: &#39;Foo&#39;, age: 30 }
const userB: User = logAndReturn(userA) // error: cannot assign unknown to user

Now with generics:

const logAndReturn = &lt;T&gt;(arg: T): T =&gt; {
  console.log(arg)
  return arg
}

const userA: User = { name: &#39;Foo&#39;, age: 30 }
const userB: User = logAndReturn(userA) // fine

Now if I pass in a User I get a User back, but the function itself doesn't care what I pass in. So this function needs to be generic because the return type depends on the type of arg.


Another example, say you want a random element from an array.

function getRandomElement&lt;T&gt;(arr: T[]): T {
  const index = Math.floor(Math.random() * arr.length)
  return arr[index]
}

const users: User[] = [...]
const randomUser = getRandomElement(users) // returns object of type: User

Here you declare a generic T to be the type for members of the array. Accept an argument that is an array of T, and then return T.

Again, you need a generic here because the return type depends on a type that will only be known when the function is called with a specific type elsewhere in your code.


> if I am using unrestricted generics, how can i use the arg inside the function?

Your function typically wont care what the type is, but something downstream will. For example, in my getRandomElement function above it doesn't matter what the elements in the array actually are to this function, but that just sets the type that will be returned.

If you want to access specific fields of those values, then that is when you need to constrain it.

function filterCool&lt;T extends { isCool: boolean }&gt;(arr: T[]): T {
  return arr.filter(item =&gt; item.isCool)
}

const users = [{ name: &quot;Abc&quot;, isCool: true }, { name: &quot;Def&quot;, isCool: false }]
const coolUsers = filterCool(users) // { name: string, isCool: boolean }[]

Here we use T extends { isCool: boolean } because the function requires isCool to work, but we want to allow other fields as well.


> loosely speaking, generics is about the ability to handle multiple types safely rather then the type itself

A better way to say it is that generics allow you accept a type that may differ throughout your program, and it allows to reference whatever that type is elsewhere.

When one type in a function depends on another type in that function, that usually means you want a generic function.

huangapple
  • 本文由 发表于 2023年5月18日 00:13:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76274151.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定