در بسیاری موارد (مانند سیستمهای Multi Tenant) لازم هست تا مانع از این شویم که دادههای کاربران با هم تداخل پیدا کند و یا آنها بتوانند به دادههای هم دسترسی داشته باشند. مثلا میخواهیم کاربران هر شعبه از سازمان، تنها به اطلاعات شعبه خودشان دسترسی داشته باشند. یک کار ساده، پردردسر و بسیار بد آن است که از برنامه نویسها بخواهیم در هر کوئری عبارتی را اضافه کنند که سطح دسترسی را چک کند. اما اگر برنامه نویس جایی فراموش کرد چی؟ اگر سیاست دسترسی پیچیدهتر بود و مبنی بر پارامترهای مختلف محاسبه میشد چه خواهد شد؟ این راهکار در حجم بزرگ غیر مطمئن و غیرقابل نگهداری است.
در EF6 قابلیتی به نام Interception وجود دارد که با استفاده از آن میتوان سیاست دسترسی به داده را در لایههای پایینی طراحی کرد. در این روش برنامه نویس لایه هایی بالا، بدون آنکه درگیر مفاهیمی مانند Tenant و سیاستها بشود، میتواند به راحتی کوئری هایش را تولید کند. سپس EF به طور خودکار تغییری در کوئریها خواهد داد تا دسترسیهای لازم رعایت کرده باشد. برای اینکار میتوانید از کتابخانه EntityFramework.DynamicFilters استفاده کنید.
این روش هم علی رغم همه مزایا معایبی هم دارد. اگر بخواهیم از همین پایگاه داده استفاده کنیم ولی در محیط دات نت نباشیم و یا از EF6 استفاده نکنیم، دوباره مشکلات اغاز میشوند. سیاستها را باید در همه جا کپی کنیم و در صورت لزوم هم، مجددا همه را تغییر دهیم.
در SQL Server 2016 قابلیتی به نام Row Level Security وجود دارد، که به ما اجازه میدهد سیاستهای دسترسی با داده را در لایه پایگاه داده متمرکز کنیم. در این صورت اپلیکشنها هیچگونه آگاهی ایی نسبت به سیاستها نخواهند داشت و درگیر این مفاهیم در سطح کد نخواهیم بود. همچنین در صورت لزوم به تغییر سیاست ها، فقط لازم است تغییراتی را در پایگاه داده بدهیم. با این روش، به هر طریقی و از هر ابزاری که به پایگاه داده کوئری هایمان را ارسال کنیم، سیاستهای دسترسی به داده اعمال خواهند شد و امنیت بالا و البته ریزدانه ای (granular) را خواهیم داشت.
در مثال زیر خواهیم دید که چگونه میتوان با استفاده از EF6 از ویژگی RLS بهره برد. این مثال یکی دیگر از کاربردهای Interception را نیز توضیح میدهد.
اشتراکها
درک بهتر websockets با aspnetcore
نظرات مطالب
معرفی ASP.NET Identity
با نصب پکیجهای مربوط به ASP.NET Identity و غیرفعال کردن Forms Auth میتونید همچین کاری بکنید اما توصیه نمیشه. سیستم Identity اکثر عملیاتش رو بصورت Async انجام میده که نیاز به NET 4.5. داره. دلایل دیگه ای هم وجود داره که اگر یک جستجوی ساده در اینترنت بکنید مطالب خوبی در این باره پیدا میکنید، مثلا لینک زیر:
http://stackoverflow.com/questions/19237285/using-asp-net-identity-in-mvc-4
http://stackoverflow.com/questions/19237285/using-asp-net-identity-in-mvc-4
در نگارشهای دیگر ASP.NET، برای دسترسی به اطلاعات درخواست وب جاری، میتوان از خاصیت استاتیک System.Web.HttpContext.Current استفاده کرد. با حذف شدن System.Web از ASP.NET Core و همچنین بهبود طراحی آن جهت سازگاری کامل با مفاهیم تزریق وابستگیها، دیگر روش استفادهی مستقیم از خواص استاتیک توصیه نشده و بجای آن تزریق اینترفیس ویژهی IHttpContextAccessor توصیه میشود.
دسترسی به اطلاعات درخواست وب جاری در ASP.NET Core
برای دسترسی به اطلاعات درخواست جاری در ASP.NET Core، میتوان از طریق تزریق سرویس جدید IHttpContextAccessor اقدام کرد. این اینترفیس دارای تک خاصیت HttpContext است که به صورت پیش فرض جزو سرویسهای از پیش ثبت شدهی ASP.NET Core نیست و برای اینکه تزریق وابستگیها در اینجا به درستی صورت گیرد، طول عمر این سرویس باید به صورت singleton تنظیم شود:
روش کارکرد این سرویس نیز به صورت ذیل است:
- هر زمانیکه درخواست جدیدی برای پردازش فرا میرسد، IHttpContextFactory کار ایجاد یک HttpContext جدید را آغاز میکند.
- اگر سرویس IHttpContextAccessor پیشتر ثبت شده باشد، IHttpContextFactory کار مقدار دهی HttpContext آنرا نیز انجام میدهد.
- اینجا شاید این سؤال مطرح شود که طول عمر IHttpContextAccessor «باید» به صورت singleton ثبت شود. پس این سرویس چگونه میتواند HttpContextهای مختلفی را شامل شود؟ کلاس HttpContextAccessor که پیاده سازی کنندهی IHttpContextAccessor است، دارای یک خاصیت AsyncLocal است که از این خاصیت جهت ذخیره سازی اطلاعات Contextهای مختلف استفاده میشود. بنابراین کلاس HttpContextAccessor دارای طول عمر singleton است، اما خاصیت AsyncLocal آن دارای طول عمری محدود به یک درخواست (request scoped) میباشد.
بنابراین به صورت خلاصه:
- هرجایی که نیاز به اطلاعات HTTP context وجود داشت، از تزریق اینترفیس IHttpContextAccessor استفاده کنید.
- ثبت سرویس IHttpContextAccessor را در ابتدای برنامه فراموش نکنید.
- طول عمر سرویس ثبت شدهی IHttpContextAccessor باید singleton باشد.
یک نکته: اگر از ASP.NET Core Identity استفاده میکنید، متد services.AddIdentity کار ثبت سرویس IHttpContextAccessor را نیز انجام میدهد.
یک مثال: ذخیره سازی اطلاعاتی با طول عمر کوتاه در HttpContext و سپس دسترسی به آنها در کلاسهای دیگر برنامه
استفادهی از مجموعهی Items شیء HttpContext، یکی از روشهایی است که از آن میتوان جهت ذخیره سازی اطلاعات موقتی و محدود به طول عمر درخواست جاری استفاده کرد. برای مثال در یک کنترلر و اکشن متدی خاص، دو key/value جدید را به آن اضافه میکنیم:
سپس جهت دسترسی به این اطلاعات در یک کلاس دیگر میتوان به صورت ذیل عمل کرد:
در اینجا در کلاسی قرار داریم که مستقیما ارتباطی به کنترلر جاری نداشته و دسترسی مستقیمی به خاصیت HttpContext آن ندارد. بنابراین برای دسترسی به اطلاعات موجود در HttpContext جاری میتوان سرویس IHttpContextAccessor را به سازندهی این کلاس تزریق کرد و سپس با کمک خاصیت contextAccessor.HttpContext آن، به اطلاعات مدنظر دسترسی یافت.
دسترسی به اطلاعات درخواست وب جاری در ASP.NET Core
برای دسترسی به اطلاعات درخواست جاری در ASP.NET Core، میتوان از طریق تزریق سرویس جدید IHttpContextAccessor اقدام کرد. این اینترفیس دارای تک خاصیت HttpContext است که به صورت پیش فرض جزو سرویسهای از پیش ثبت شدهی ASP.NET Core نیست و برای اینکه تزریق وابستگیها در اینجا به درستی صورت گیرد، طول عمر این سرویس باید به صورت singleton تنظیم شود:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }
- هر زمانیکه درخواست جدیدی برای پردازش فرا میرسد، IHttpContextFactory کار ایجاد یک HttpContext جدید را آغاز میکند.
- اگر سرویس IHttpContextAccessor پیشتر ثبت شده باشد، IHttpContextFactory کار مقدار دهی HttpContext آنرا نیز انجام میدهد.
- اینجا شاید این سؤال مطرح شود که طول عمر IHttpContextAccessor «باید» به صورت singleton ثبت شود. پس این سرویس چگونه میتواند HttpContextهای مختلفی را شامل شود؟ کلاس HttpContextAccessor که پیاده سازی کنندهی IHttpContextAccessor است، دارای یک خاصیت AsyncLocal است که از این خاصیت جهت ذخیره سازی اطلاعات Contextهای مختلف استفاده میشود. بنابراین کلاس HttpContextAccessor دارای طول عمر singleton است، اما خاصیت AsyncLocal آن دارای طول عمری محدود به یک درخواست (request scoped) میباشد.
بنابراین به صورت خلاصه:
- هرجایی که نیاز به اطلاعات HTTP context وجود داشت، از تزریق اینترفیس IHttpContextAccessor استفاده کنید.
- ثبت سرویس IHttpContextAccessor را در ابتدای برنامه فراموش نکنید.
- طول عمر سرویس ثبت شدهی IHttpContextAccessor باید singleton باشد.
یک نکته: اگر از ASP.NET Core Identity استفاده میکنید، متد services.AddIdentity کار ثبت سرویس IHttpContextAccessor را نیز انجام میدهد.
یک مثال: ذخیره سازی اطلاعاتی با طول عمر کوتاه در HttpContext و سپس دسترسی به آنها در کلاسهای دیگر برنامه
استفادهی از مجموعهی Items شیء HttpContext، یکی از روشهایی است که از آن میتوان جهت ذخیره سازی اطلاعات موقتی و محدود به طول عمر درخواست جاری استفاده کرد. برای مثال در یک کنترلر و اکشن متدی خاص، دو key/value جدید را به آن اضافه میکنیم:
public IActionResult ProcessForm() { HttpContext.Items["firstname"] = "Vahid"; HttpContext.Items["lastname"] = "N."; return View(); }
public class MyHelperClass { private readonly IHttpContextAccessor _contextAccessor; public MyHelperClass(IHttpContextAccessor contextAccessor) { _contextAccessor = contextAccessor; } public string DoWork() { string firstName = _contextAccessor.HttpContext.Items["firstname"].ToString(); string lastName = _contextAccessor.HttpContext.Items["lastname"].ToString(); return $"Hello {firstName} {lastName}!"; } }
Here’s a summary of what’s new in this preview release:
- Request decompression middleware
- Output caching middleware
- Updates to rate limiting middleware
- Kestrel support for WebSockets over HTTP/2
- Kestrel performance improvements on high core machines
- Support for logging additional request headers in W3CLogger
- Empty Blazor project templates
- System.Security.Cryptography support on WebAssembly
- Blazor custom elements no longer experimental
- Experimental
QuickGrid
component for Blazor - gRPC JSON transcoding multi-segment parameters
-
MapGroup
support for more extension methods
نظرات مطالب
بررسی روش آپلود فایلها در ASP.NET Core
خلاصه نکات این مطلب در برنامههای ASP.NET Core
ابتدا بستهی نیوگت DNTCommon.Web.Core را نصب کنید:
سپس مثالی از UploadFileExtensions آنرا در اینجا میتوانید مشاهده کنید. همچنین قسمت ذخیره سازی فایل آن نیز تبدیل به یک سرویس به نام IUploadFileService شدهاست.
ابتدا بستهی نیوگت DNTCommon.Web.Core را نصب کنید:
PM> Install-Package DNTCommon.Web.Core
بخش عمدهای از مهندسی نرم افزار، مربوط به ساخت کامپوننتهایی است که نه تنها به خوبی و مستحکم توسعه داده شدهاند، بلکه قابلیت استفاده دوباره را نیز دارند.
کامپوننتهایی که قادر هستند بر روی دادههای فعلی و همچنین دادههای آینده، کار کنند، قابلیتهای انعطاف پذیری را برای ساخت سیستمهای نرم افزاری بزرگ در اختیار شما قرار خواهند داد.
در زبان هایی نظیر جاوا و سی شارپ، یکی از ابزارهای اصلی برای ساخت کامپوننتهایی با قابلیت استفاده مجدد، "جنریکها" میباشد که امکان ساخت کامپوننتهایی را میدهند که با انواع دادههای متنوعی به جای یک نوع داده، کار میکنند.
برای شروع به تابع زیر توجه کنید:
function identity(arg: number): number { return arg; }
تابع identity هر آنچه را که به عنوان پارامتر به آرگومان آن ارسال کنیم، بازگشت خواهد داد. میتوانید آن را به مانند دستور "echo" در نظر بگیرید.
بدون استفاده از جنریک ها، باید برای هر نوع داده، یک تابع جدید و یا تابعی را به صورت کلی زیر در نظر بگیریم:
function identity(arg: any): any { return arg; }
در تابع بالا از نوع any استفاده شده است. با استفاده از any، قطعا تابع بالا به صورت عمومی خواهد بود و تمام نوع دادهها را به عنوان آرگومان خواهد پذیرفت. ولی در واقع ما اطلاعات مربوط به اینکه نوع داده بازگشتی توسط تابع چه چیزی است را از دست خواهیم داد.
برای مثال اگر یک عدد را به آن ارسال کنیم، تنها متوجه خواهیم شد که نوع آن any میباشد؛ بنابراین به روشی نیاز داریم تا بتوانیم نوع داده آرگومانهای تابع مورد نظر را کنترل کنیم.
در پیاده سازی زیر، ما از یک type variable خاصی استفاده خواهیم کرد که به جای مقادیر برای انوع دادهها مورد استفاده قرار میگیرد.
function identity<T>(arg: T): T { return arg; }
در تابع بالا با از T به عنوان یک type variable استفاده کردهایم که امکان گرفتن انواع دادههایی را (برای مثال number) که توسط کاربر مهیا میشود، به ما خواهد داد.
این پیاده سازی از تابع identity، تحت عنوان تابع جنریک مطرح میشود که برای دامنهی عظیمی از انواع دادهها میتواند مورد استفاده قرار گیرد و بر خلاف پیاده سازی قبل که از any استفاده کردهایم، در این حالت دیگر اطلاعات نوع داده را از دست نخواهیم داد.
برای استفاده از تابع فوق ما دو روش را پیش رو خواهیم داشت:
- ارسال تمام آرگومانها که شامل آرگومان نوع داده هم میباشد
let output = identity<string>("myString"); // type of output will be 'string'
در کد بالا ما به صراحت T را با نوع داده string با استفاده از < > مقدار دهی کردهایم.
- روش دوم که شاید استفاده رایج از توابع جنریک هم هست، استفاده از امکان type argument inference میباشد.
let output = identity("myString"); // type of output will be 'string'
در کد بالا اینبار به صورت صریح نوع T را مشخص نکردهایم و کامپایلر باتوجه به "myString"، نوع T را تعیین خواهد کرد. درحالیکه استفاده از امکان type argument inference خیلی مفید میباشد و کد را خیلی کم حجم و خوانا در اختیار ما قرار میدهد، ولی در مثالهای پیچیده، امکان این وجود دارد که کامپایلر در تشخیص نوع داده، با خطا مواجه شود. در این صورت استفاده از روش اول مفید خواهد بود.
در ادامه اگر قصد لاگ کردن Length مربوط به آرگومان arg را در هر بار فراخوانی تابع داشته باشیم، میبایستی به شکل زیر عمل کنیم:
function loggingIdentity<T>(arg: T): T { console.log(arg.length); // Error: T doesn't have .length return arg; }
همانطور که انتظار داشتیم، کامپایلر خطایی مبنی بر نداشتن عضوی تحت عنوان length برای آرگومان arg را نمایش خواهد داد. همانطور که قبلا نیز اشاره کردیم، T جانشینی برای تمام نوع دادهها خواهد بود؛ بنابراین در اینجا میتوانیم یک دادهی از نوع number را که عضوی بنام length ندارد، هم به این تابع پاس دهیم.
حال بیایید بگوییم که ما قصد داریم این تابع، با آرایه ای از T کار کند. در این صورت اگر با آرایهها کار کنیم، عضوی به نام length را خواهیم داشت. به پیاده سازی زیر توجه کنید:
function loggingIdentity<T>(arg: T[]): T[] { console.log(arg.length); // Array has a .length, so no more error return arg; }
کد بالا را میتوانیم به این شکل تفسیر کنیم: تابع جنریک loggingIdentity یک type parameter را تحت عنوان T و یک آرگومان را تحت عنوان arg که آرایه ای از T هست، گرفته و آرایهای از T را بازگشت خواهد داد. اگر ما آرایهای از number را به آن پاس دهیم، آرایهای از numberها را بازگشت خواهد داد.
در این حالت استفاده از T به عنوان type variable که بخشی از نوع دادههایی است که ما با آنها کار میکنیم، به جای پشتیبانی از تمام نوع دادهها، انعطاف پذیری بالایی را به ما خواهد داد.
حتی میتوانیم این مثال را به شکل زیر نیز پیاده سازی کنیم:
function loggingIdentity<T>(arg: Array<T>): Array<T> { console.log(arg.length); // Array has a .length, so no more error return arg; }
پیاده سازی بالا خیلی شبیه به پیاده سازی در سایر زبانها هم میباشد.
Generic Types
در این قسمت ما به دنبال یافتن نوع خود توابع بوده و سعی خواهیم کرد اینترفیسهای جنریک را هم پیاده سازی کنیم. نوع توابع جنریک هم بمانند توابع غیر جنریک میباشند؛ به طوری که میتوان لیستی از type parameters هایی را که در حالت function declarations موجود هستند، در ابتدا بنویسیم.
function identity<T>(arg: T): T { return arg; } let myIdentity: <T>(arg: T) => T = identity;
حتی میتوانیم نام متفاوتی را هم برای type parameter در نظر بگیرم:
function identity<T>(arg: T): T { return arg; } let myIdentity: <U>(arg: U) => U = identity;
یا حتی میتوانیم به مانند امضای یک object literal هم کد بالا را بازنویسی کنیم:
function identity<T>(arg: T): T { return arg; } let myIdentity: {<T>(arg: T): T} = identity;
حال میتوانیم این object literal را به یک اینترفیس منتقل کنیم:
interface GenericIdentityFn { <T>(arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn = identity;
کد بالا خوانایی بالاتری را نسبت به حالت قبل دارد و با تعریف یک اینترفیس به نام GenericIdentityFn و انتقال object literal به داخل آن، میتوانیم از نام اینترفیس به جای استفاده مستقیم از object literal، بهره ببریم.
حتی میتوانیم type parameter تابع جنریک خود را هم به اینترفیس منتقل کنیم.
interface GenericIdentityFn<T> { (arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn<number> = identity;
باید توجه داشت که پیاده سازی ما کمی متفاوتتر از قبل شده است.الان type parameter ما برای کل اعضای اینترفیس قابل رویت میباشد.فهم این مورد که چه زمانی type parameter را در امضای نامیدن داخل اینترفیس یا بر روی خود اینترفیس استفاده کنیم، خود میتوانید برای شرح اینکه کدام وجههای یک نوع داده جنریک هستند، مفید باشد.
نکته : امکان تعریف enumها و namespaceهای جنریک وجود ندارد.
Generic Classes
تعریف کلاسهای جنریک هم به مانند اینترفیسهای جنریک میباشد. به مثال زیر توجه کنید:
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; };
در کد بالا، استفادهای واقعی از کلاس GenericNumber قابل مشاهده است. شاید متوجه شده باشید که هیچ محدودیتی برای استفادهی نوعها برای مثال تنها از نوع number در آن نیست و میتوانید از نوع string هم به شکل زیر استفاده کنید:
let stringNumeric = new GenericNumber<string>(); stringNumeric.zeroValue = ""; stringNumeric.add = function(x, y) { return x + y; }; alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
نکته : برای اعضای استاتیک کلاس نمیتوانید از type parameter کلاس استفاده کنید.
Generic Constraints
اگر مثال اخیر را به یاد داشته باشید، شاید بعضی اوقات لازم باشد که یک تابع جنریک را تعریف کنیم تا تنها با مجموعهای از نوع دادهها کار کند که اتفاقا از امکانات این مجموعه، آگاهی داریم. در همان مثال loggingIdentity، ما نیاز داشتیم تا به خصوصیت length آرگومان arg دسترسی داشته باشیم و کامپایلر در همان ابتدا، به دلیل اینکه همه نوع دادهها از این خصوصیت برخوردار نیستند، خطایی را به ما نشان میدهد.
در ادامه تابعی را پیاده سازی میکنیم که جوابگوی تمام نوع دادهها بوده، به شرطی که حداقل خصوصیت length را داشته باشند. لذا باید نیاز خود را در قالب یک محدودیت بر آنچه که T میتواند انجام دهد، فهرست کنیم.
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; }
در کد بالا برای توصیف محدودیت خود از یک اینترفیس به نام Lengthwise استفاده کردهایم که فقط یه خصوصیت length را دارد و با استفاده از آن و کلمهی کلیدی extends، محدودیت خود را اعمال کرده ایم.
استفاده از تابع بالا:
loggingIdentity(3); // Error, number doesn't have a .length property
چون تابع جنریک ما الان محدود میباشد و با تمام نوع دادهها کار نخواهد کرد، با خطای بالا روبرو خواهیم شد.
loggingIdentity({length: 10, value: 3});
در عوض مثال بالا، محدودیت ما را به همراه دارد (داشتن خصوصیت length) و بدون هیچ خطایی جواب خواهیم گرفت.
استفاده از Type Parameterها در تعریف محدودیت
در برخی از سناریوها شاید نیاز باشد که یکی از type parameterها توسط دیگری محدود شده باشد. به مثال زیر توجه کنید:
function find<T, U extends Findable<T>>(n: T, s: U) { // errors because type parameter used in constraint // ... } find (giraffe, myAnimals);
همانطور که مشخص است، کامپایلر ما را با نشان دادن خطایی متوقف خواهد کرد. چون اجازهی استفاده از type parameter را در اعمال محدودیت، نداریم. در عوض میشود به شکل زیر عمل کرد:
function find<T>(n: T, s: Findable<T>) { // ... } find(giraffe, myAnimals);
این بار آرگومان s ما باید از نوع <Findable<T باشد که باز هم توانستهایم محدودیت خود را توسط یک type parameter بر آن یکی اعمال کنیم.
نکته : دو پیاده سازی بالا اصلا یکسان نیستند؛ نوع بازگشی در تابع اول میبایستی از نوع U میبود، ولی در پیاده سازی دوم اینگونه نیست.(در صورت نبودن خطا)
استفاده از کلاسها در جنریکها
زمانی که قصد دارید با استفاده از جنریکها، factoryها را پیاده سازی کنید، باید با استفاده از سازندهی کلاسها، به آنها اشاره کنید. به مثال زیر توجه کنید:
function create<T>(c: {new(): T; }): T { return new c(); }
تابع بالا به عنوان یک object factory میتواند مورد استفاده قرار بگیرد و نکته آن در تعریف نوع آرگومان c میباشد که باز هم به صورت object literal معرفی شده است. اگر در قسمتهای بالا به یاد داشته باشید، میتوان این مورد را هم داخل یک اینترفیس گنجاند.
به عنوان یک مثال پیشرفتهتر هم میتوان به استفاده از prototype property برای استنتاج type parameterها و تحمیل کردن ارتباط بین تابع سازنده و وهله کلاسها، اشاره کرد. به مثال زیر توجه کنید:
class BeeKeeper { hasMask: boolean; } class ZooKeeper { nametag: string; } class Animal { numLegs: number; } class Bee extends Animal { keeper: BeeKeeper; } class Lion extends Animal { keeper: ZooKeeper; } function findKeeper<A extends Animal, K> (a: {new(): A; prototype: {keeper: K}}): K { return a.prototype.keeper; }
در کد بالا از دو کلاس BeeKeeper و ZooKeeper برای نوع بازگشتی متدهای موجود در کلاسهای Bee و Lion استفاده شدهاست. کلاس Animal به عنوان کلاس پایه دو کلاس Bee و Lion که یک خصوصیت numLegs دارد، تعریف شدهاست. از تابع جنریک findKeeper برای مشخص کردن نگهبان مرتبط با Animal ای که به عنوان type parameter توسط A مشخص میشود، استفاده میگردد. محدودیتی که بر روی A اعمال شده است نشان دهندهی این است که نوع دادهی مورد نظر باید حتما یک Animal باشد و همچنین با اعمال محدودیتی که در قالب object literal مشخص است، تعیین شده است که نوع مورد نظر باید یک کلاس باشد و در نهایت با استفاده از prototype مشخص کردهایم که متدی به نام Keeper آن کلاس، باید نوع برگشتی از نوع K را که به عنوان type parameter مطرح شدهی در امضای تابع است، دارا باشد. K نشان دهنده نوع داده بازگشتی این تابع جنریک نیز میباشد.
استفاده از تابع بالا:
findKeeper(Lion).nametag; // typechecks!
بله همانطور که مشخص است، type parameterهای مورد نظر به اصطلاح infer شدهاند و خصوصیت nametag نشان از این دارد که ZooKeeper به صورت خودکار به عنوان نوع داده K تشخیص داده شده است.